diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-17 23:44:34 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-17 23:44:34 +0000 |
commit | 633698db41fb44ade5f64c6597b1065099bc52b9 (patch) | |
tree | cad60838646a4f657e399167ad18e4b98c486cfb | |
parent | 8621715ceeb4533aca1d27d639cd2e47c33cd961 (diff) | |
download | chromium_src-633698db41fb44ade5f64c6597b1065099bc52b9.zip chromium_src-633698db41fb44ade5f64c6597b1065099bc52b9.tar.gz chromium_src-633698db41fb44ade5f64c6597b1065099bc52b9.tar.bz2 |
Refactors renderer related classes for better sharing.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/2089012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47473 0039d316-1c4b-4281-b951-d872f2087c98
25 files changed, 1695 insertions, 1886 deletions
diff --git a/chrome/browser/views/extensions/extension_installed_bubble.cc b/chrome/browser/views/extensions/extension_installed_bubble.cc index 29aa69a..33f4dcf 100644 --- a/chrome/browser/views/extensions/extension_installed_bubble.cc +++ b/chrome/browser/views/extensions/extension_installed_bubble.cc @@ -14,7 +14,7 @@ #include "chrome/browser/views/browser_actions_container.h" #include "chrome/browser/views/frame/browser_view.h" #include "chrome/browser/views/location_bar/location_bar_view.h" -#include "chrome/browser/views/tabs/base_tab_renderer.h" +#include "chrome/browser/views/tabs/base_tab.h" #include "chrome/browser/views/tabs/base_tab_strip.h" #include "chrome/browser/views/toolbar_view.h" #include "chrome/browser/web_applications/web_app.h" @@ -318,7 +318,7 @@ void ExtensionInstalledBubble::ShowInternal() { DCHECK(reference_view); } else if (type_ == EXTENSION_APP) { BaseTabStrip* tabstrip = browser_view->tabstrip(); - BaseTabRenderer* tab = tabstrip->GetSelectedBaseTab(); + BaseTab* tab = tabstrip->GetSelectedBaseTab(); DCHECK(tab->data().app); reference_view = tab; } diff --git a/chrome/browser/views/frame/glass_browser_frame_view.cc b/chrome/browser/views/frame/glass_browser_frame_view.cc index 2e009c8..6838a3e 100644 --- a/chrome/browser/views/frame/glass_browser_frame_view.cc +++ b/chrome/browser/views/frame/glass_browser_frame_view.cc @@ -10,6 +10,7 @@ #include "chrome/browser/browser_theme_provider.h" #include "chrome/browser/views/frame/browser_view.h" #include "chrome/browser/views/tabs/side_tab_strip.h" +#include "chrome/browser/views/tabs/tab.h" #include "chrome/browser/views/tabs/tab_strip.h" #include "gfx/canvas.h" #include "gfx/icon_util.h" @@ -257,7 +258,7 @@ void GlassBrowserFrameView::PaintToolbarBackground(gfx::Canvas* canvas) { // Draw the toolbar background, setting src_y of the paint to the tab // strip height as the toolbar background begins at the top of the tabs. int src_y = browser_view_->UseVerticalTabs() - ? TabRenderer::GetMinimumUnselectedSize().height() + ? Tab::GetMinimumUnselectedSize().height() : browser_view_->GetTabStripHeight() - 1; canvas->TileImageInt(*theme_toolbar, 0, src_y, toolbar_bounds.x() - 1, toolbar_bounds.y() + 2, diff --git a/chrome/browser/views/tabs/base_tab.cc b/chrome/browser/views/tabs/base_tab.cc new file mode 100644 index 0000000..070e1ee --- /dev/null +++ b/chrome/browser/views/tabs/base_tab.cc @@ -0,0 +1,530 @@ +// Copyright (c) 2010 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. + +#include "chrome/browser/views/tabs/base_tab.h" + +#include <limits> + +#include "app/animation_container.h" +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "app/slide_animation.h" +#include "app/throb_animation.h" +#include "base/command_line.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/views/tabs/tab_controller.h" +#include "chrome/common/chrome_switches.h" +#include "gfx/canvas.h" +#include "gfx/favicon_size.h" +#include "gfx/font.h" +#include "grit/app_resources.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "views/controls/button/image_button.h" + +#ifdef WIN32 +#include "app/win_util.h" +#endif + +// How long the pulse throb takes. +static const int kPulseDurationMs = 200; + +// How long the hover state takes. +static const int kHoverDurationMs = 90; + +static SkBitmap* waiting_animation_frames = NULL; +static SkBitmap* loading_animation_frames = NULL; +static int loading_animation_frame_count = 0; +static int waiting_animation_frame_count = 0; +static int waiting_to_loading_frame_count_ratio = 0; + +// Close button images. +static SkBitmap* close_button_n = NULL; +static SkBitmap* close_button_h = NULL; +static SkBitmap* close_button_p = NULL; + +static SkBitmap* crashed_fav_icon = NULL; + +namespace { + +//////////////////////////////////////////////////////////////////////////////// +// 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 views::ImageButton { + public: + explicit TabCloseButton(views::ButtonListener* listener) + : views::ImageButton(listener) { + } + virtual ~TabCloseButton() {} + + virtual bool OnMousePressed(const views::MouseEvent& event) { + bool handled = ImageButton::OnMousePressed(event); + // Explicitly mark midle-mouse clicks as non-handled to ensure the tab + // sees them. + return event.IsOnlyMiddleMouseButton() ? false : handled; + } + + // 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 views::MouseEvent& event) { + CustomButton::OnMouseEntered(event); + GetParent()->OnMouseEntered(event); + } + + virtual void OnMouseExited(const views::MouseEvent& event) { + CustomButton::OnMouseExited(event); + GetParent()->OnMouseExited(event); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TabCloseButton); +}; + +} // namespace + +// static +int BaseTab::close_button_width_ = 0; +// static +int BaseTab::close_button_height_ = 0; +// static +int BaseTab::loading_animation_size_ = 0; + +// static +gfx::Font* BaseTab::font_ = NULL; +// static +int BaseTab::font_height_ = 0; + +//////////////////////////////////////////////////////////////////////////////// +// FaviconCrashAnimation +// +// A custom animation subclass to manage the favicon crash animation. +class BaseTab::FavIconCrashAnimation : public LinearAnimation, + public AnimationDelegate { + public: + explicit FavIconCrashAnimation(BaseTab* target) + : ALLOW_THIS_IN_INITIALIZER_LIST(LinearAnimation(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: + BaseTab* target_; + + DISALLOW_COPY_AND_ASSIGN(FavIconCrashAnimation); +}; + +BaseTab::BaseTab(TabController* controller) + : controller_(controller), + closing_(false), + dragging_(false), + loading_animation_frame_(0), + throbber_disabled_(false), + theme_provider_(NULL), + fav_icon_hiding_offset_(0), + should_display_crashed_favicon_(false) { + BaseTab::InitResources(); + + // Add the Close Button. + TabCloseButton* close_button = new TabCloseButton(this); + close_button_ = close_button; + close_button->SetImage(views::CustomButton::BS_NORMAL, close_button_n); + close_button->SetImage(views::CustomButton::BS_HOT, close_button_h); + close_button->SetImage(views::CustomButton::BS_PUSHED, close_button_p); + close_button->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_CLOSE_TAB)); + close_button->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE)); + close_button->SetAnimationDuration(0); + AddChildView(close_button); + + SetContextMenuController(this); +} + +BaseTab::~BaseTab() { +} + +void BaseTab::SetData(const TabRendererData& data) { + TabRendererData old(data_); + data_ = data; + + if (data_.crashed) { + if (!should_display_crashed_favicon_ && !IsPerformingCrashAnimation()) + StartCrashAnimation(); + } else { + if (IsPerformingCrashAnimation()) + StopCrashAnimation(); + ResetCrashedFavIcon(); + } + + // Sets the accessible name for the tab. + SetAccessibleName(UTF16ToWide(data_.title)); + + DataChanged(old); + + Layout(); +} + +void BaseTab::UpdateLoadingAnimation(TabRendererData::NetworkState state) { + // If this is an extension app and a command line flag is set, + // then disable the throbber. + throbber_disabled_ = data().app && + CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsNoThrob); + + if (throbber_disabled_) + return; + + if (state == data_.network_state && + state == TabRendererData::NETWORK_STATE_NONE) { + // If the network state is none and hasn't changed, do nothing. Otherwise we + // need to advance the animation frame. + return; + } + + TabRendererData::NetworkState old_state = data_.network_state; + data_.network_state = state; + AdvanceLoadingAnimation(old_state, state); +} + +void BaseTab::StartPulse() { + if (!pulse_animation_.get()) { + pulse_animation_.reset(new ThrobAnimation(this)); + pulse_animation_->SetSlideDuration(kPulseDurationMs); + if (animation_container_.get()) + pulse_animation_->SetContainer(animation_container_.get()); + } + pulse_animation_->Reset(); + pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); +} + +void BaseTab::StopPulse() { + if (!pulse_animation_.get()) + return; + + pulse_animation_->Stop(); // Do stop so we get notified. + pulse_animation_.reset(NULL); +} + +bool BaseTab::IsSelected() const { + return controller() ? controller()->IsTabSelected(this) : true; +} + +void BaseTab::OnMouseEntered(const views::MouseEvent& e) { + if (!hover_animation_.get()) { + hover_animation_.reset(new SlideAnimation(this)); + hover_animation_->SetContainer(animation_container_.get()); + hover_animation_->SetSlideDuration(kHoverDurationMs); + } + hover_animation_->SetTweenType(Tween::EASE_OUT); + hover_animation_->Show(); +} + +void BaseTab::OnMouseExited(const views::MouseEvent& e) { + hover_animation_->SetTweenType(Tween::EASE_IN); + hover_animation_->Hide(); +} + +bool BaseTab::OnMousePressed(const views::MouseEvent& event) { + if (event.IsOnlyLeftMouseButton()) { + // Store whether or not we were selected just now... we only want to be + // able to drag foreground tabs, so we don't start dragging the tab if + // it was in the background. + bool just_selected = !IsSelected(); + if (just_selected) + controller()->SelectTab(this); + controller()->MaybeStartDrag(this, event); + } + return true; +} + +bool BaseTab::OnMouseDragged(const views::MouseEvent& event) { + controller()->ContinueDrag(event); + return true; +} + +void BaseTab::OnMouseReleased(const views::MouseEvent& event, bool canceled) { + // Notify the drag helper that we're done with any potential drag operations. + // Clean up the drag helper, which is re-created on the next mouse press. + // In some cases, ending the drag will schedule the tab for destruction; if + // so, bail immediately, since our members are already dead and we shouldn't + // do anything else except drop the tab where it is. + if (controller()->EndDrag(canceled)) + return; + + // Close tab on middle click, but only if the button is released over the tab + // (normal windows behavior is to discard presses of a UI element where the + // releases happen off the element). + if (event.IsMiddleMouseButton() && HitTest(event.location())) + controller()->CloseTab(this); +} + +bool BaseTab::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) { + if (data_.title.empty()) + return false; + + std::wstring title = UTF16ToWide(data_.title); + // Only show the tooltip if the title is truncated. + if (font_->GetStringWidth(title) > title_bounds().width()) { + *tooltip = title; + return true; + } + return false; +} + +bool BaseTab::GetAccessibleRole(AccessibilityTypes::Role* role) { + DCHECK(role); + + *role = AccessibilityTypes::ROLE_PAGETAB; + return true; +} + +ThemeProvider* BaseTab::GetThemeProvider() { + ThemeProvider* tp = View::GetThemeProvider(); + return tp ? tp : theme_provider_; +} + +void BaseTab::AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, + TabRendererData::NetworkState 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 (state != old_state) { + loading_animation_frame_ = loading_animation_frame_count - + (loading_animation_frame_ / waiting_to_loading_frame_count_ratio); + } + + if (state != TabRendererData::NETWORK_STATE_NONE) { + loading_animation_frame_ = ++loading_animation_frame_ % + ((state == TabRendererData::NETWORK_STATE_WAITING) ? + waiting_animation_frame_count : loading_animation_frame_count); + } else { + loading_animation_frame_ = 0; + } + SchedulePaint(); +} + +void BaseTab::PaintIcon(gfx::Canvas* canvas, int x, int y) { + if (base::i18n::IsRTL()) { + if (!data().favicon.isNull()) + x = width() - x - data().favicon.width(); + else + x = width() - x - kFavIconSize; + } + + int favicon_x = x; + if (!data().favicon.isNull() && data().favicon.width() != kFavIconSize) + favicon_x += (data().favicon.width() - kFavIconSize) / 2; + + if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { + SkBitmap* frames = + (data().network_state == TabRendererData::NETWORK_STATE_WAITING) ? + waiting_animation_frames : loading_animation_frames; + int image_size = frames->height(); + int image_offset = loading_animation_frame_ * image_size; + int dst_y = (height() - image_size) / 2; + canvas->DrawBitmapInt(*frames, image_offset, 0, image_size, + image_size, favicon_x, dst_y, image_size, image_size, + false); + } else { + canvas->save(); + canvas->ClipRectInt(0, 0, width(), height()); + if (should_display_crashed_favicon_) { + canvas->DrawBitmapInt(*crashed_fav_icon, 0, 0, + crashed_fav_icon->width(), + crashed_fav_icon->height(), + favicon_x, + (height() - crashed_fav_icon->height()) / 2 + + fav_icon_hiding_offset_, + kFavIconSize, kFavIconSize, + true); + } else { + if (!data().favicon.isNull()) { + // TODO(pkasting): Use code in tab_icon_view.cc:PaintIcon() (or switch + // to using that class to render the favicon). + int size = data().favicon.width(); + canvas->DrawBitmapInt(data().favicon, 0, 0, + data().favicon.width(), + data().favicon.height(), + x, y + fav_icon_hiding_offset_, size, size, + true); + } + } + canvas->restore(); + } +} + +void BaseTab::PaintTitle(gfx::Canvas* canvas, SkColor title_color) { + // Paint the Title. + string16 title = data().title; + if (title.empty()) { + title = data().loading ? + l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : + TabContents::GetDefaultTitle(); + } else { + Browser::FormatTitleForDisplay(&title); + } + + canvas->DrawStringInt(UTF16ToWideHack(title), *font_, title_color, + title_bounds().x(), title_bounds().y(), + title_bounds().width(), title_bounds().height()); +} + +void BaseTab::AnimationProgressed(const Animation* animation) { + SchedulePaint(); +} + +void BaseTab::AnimationCanceled(const Animation* animation) { + SchedulePaint(); +} + +void BaseTab::AnimationEnded(const Animation* animation) { + SchedulePaint(); +} + +void BaseTab::ButtonPressed(views::Button* sender, const views::Event& event) { + DCHECK(sender == close_button_); + controller()->CloseTab(this); +} + +void BaseTab::ShowContextMenu(views::View* source, + const gfx::Point& p, + bool is_mouse_gesture) { + controller()->ShowContextMenu(this, p); +} + +void BaseTab::ThemeChanged() { + views::View::ThemeChanged(); + LoadThemeImages(); +} + +void BaseTab::SetFavIconHidingOffset(int offset) { + fav_icon_hiding_offset_ = offset; + SchedulePaint(); +} + +void BaseTab::DisplayCrashedFavIcon() { + should_display_crashed_favicon_ = true; +} + +void BaseTab::ResetCrashedFavIcon() { + should_display_crashed_favicon_ = false; +} + +void BaseTab::StartCrashAnimation() { + if (!crash_animation_.get()) + crash_animation_.reset(new FavIconCrashAnimation(this)); + crash_animation_->Stop(); + crash_animation_->Start(); +} + +void BaseTab::StopCrashAnimation() { + if (!crash_animation_.get()) + return; + crash_animation_->Stop(); +} + +bool BaseTab::IsPerformingCrashAnimation() const { + return crash_animation_.get() && crash_animation_->is_animating(); +} + +// static +void BaseTab::InitResources() { + static bool initialized = false; + if (initialized) + return; + + initialized = true; + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + crashed_fav_icon = rb.GetBitmapNamed(IDR_SAD_FAVICON); + + 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(); + + // 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); + loading_animation_size_ = loading_animation_frames->height(); + 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(); + + // We get a DIV0 further down when the throbber is replaced by an image which + // is taller than wide. In this case we cannot deduce an animation sequence + // from it since we assume that each animation frame has the width of the + // image's height. + if (loading_animation_frame_count == 0) { +#ifdef WIN32 + // TODO(idanan): Remove this when we have a way to handle theme errors. + // See: http://code.google.com/p/chromium/issues/detail?id=12531 For now, + // this is Windows-specific because some users have downloaded a DLL from + // outside of Google to override the theme. + std::wstring text = l10n_util::GetString(IDS_RESOURCE_ERROR); + std::wstring caption = l10n_util::GetString(IDS_RESOURCE_ERROR_CAPTION); + UINT flags = MB_OK | MB_ICONWARNING | MB_TOPMOST; + win_util::MessageBox(NULL, text, caption, flags); +#endif + CHECK(loading_animation_frame_count) << + "Invalid throbber size. Width = " << + loading_animation_frames->width() << ", height = " << + 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; + // TODO(beng): eventually remove this when we have a proper themeing system. + // themes not supporting IDR_THROBBER_WAITING are causing this + // value to be 0 which causes DIV0 crashes. The value of 5 + // matches the current bitmaps in our source. + if (waiting_to_loading_frame_count_ratio == 0) + waiting_to_loading_frame_count_ratio = 5; + + font_ = new gfx::Font(rb.GetFont(ResourceBundle::BaseFont)); + font_height_ = font_->height(); +} + +// static +void BaseTab::LoadThemeImages() { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + loading_animation_frames = rb.GetBitmapNamed(IDR_THROBBER); + waiting_animation_frames = rb.GetBitmapNamed(IDR_THROBBER_WAITING); + loading_animation_size_ = loading_animation_frames->height(); +} diff --git a/chrome/browser/views/tabs/base_tab.h b/chrome/browser/views/tabs/base_tab.h new file mode 100644 index 0000000..678594d --- /dev/null +++ b/chrome/browser/views/tabs/base_tab.h @@ -0,0 +1,220 @@ +// Copyright (c) 2010 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_VIEWS_TABS_BASE_TAB_H_ +#define CHROME_BROWSER_VIEWS_TABS_BASE_TAB_H_ + +#include "app/animation.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "chrome/browser/views/tabs/tab_renderer_data.h" +#include "views/controls/button/button.h" +#include "views/view.h" + +class AnimationContainer; +class BaseTab; +class SlideAnimation; +class TabController; +class ThrobAnimation; + +namespace gfx { +class Font; +} // namespace gfx + +namespace views { +class ImageButton; +} // namespace views + +// Base class for tab renderers. +class BaseTab : public AnimationDelegate, + public views::ButtonListener, + public views::ContextMenuController, + public views::View { + public: + explicit BaseTab(TabController* controller); + ~BaseTab(); + + // Sets the data this tabs displays. Invokes DataChanged for subclasses to + // update themselves appropriately. + void SetData(const TabRendererData& data); + const TabRendererData& data() const { return data_; } + + // Sets the network state. If the network state changes NetworkStateChanged is + // invoked. + virtual void UpdateLoadingAnimation(TabRendererData::NetworkState state); + + // Starts/Stops a pulse animation. + void StartPulse(); + void StopPulse(); + + // Used to set/check whether this Tab is being animated closed. + void set_closing(bool closing) { closing_ = closing; } + bool closing() const { return closing_; } + + // See description above field. + void set_dragging(bool dragging) { dragging_ = dragging; } + bool dragging() const { return dragging_; } + + // Sets the container all animations run from. + void set_animation_container(AnimationContainer* container) { + animation_container_ = container; + } + AnimationContainer* animation_container() const { + return animation_container_.get(); + } + + // Set the theme provider - because we get detached, we are frequently + // outside of a hierarchy with a theme provider at the top. This should be + // called whenever we're detached or attached to a hierarchy. + void set_theme_provider(ThemeProvider* provider) { + theme_provider_ = provider; + } + + // Returns true if the tab is selected. + bool IsSelected() const; + + // views::View overrides: + virtual void OnMouseEntered(const views::MouseEvent& event); + virtual void OnMouseExited(const views::MouseEvent& event); + virtual bool OnMousePressed(const views::MouseEvent& event); + virtual bool OnMouseDragged(const views::MouseEvent& event); + virtual void OnMouseReleased(const views::MouseEvent& event, + bool canceled); + virtual bool GetTooltipText(const gfx::Point& p, std::wstring* tooltip); + virtual bool GetAccessibleRole(AccessibilityTypes::Role* role); + virtual ThemeProvider* GetThemeProvider(); + + protected: + // Invoked from SetData after |data_| has been updated to the new data. + virtual void DataChanged(const TabRendererData& old) {} + + // Invoked if data_.network_state changes, or the network_state is not none. + virtual void AdvanceLoadingAnimation(TabRendererData::NetworkState old_state, + TabRendererData::NetworkState state); + + TabController* controller() const { return controller_; } + + // Returns the pulse animation. The pulse animation is non-null if StartPulse + // has been invoked. + ThrobAnimation* pulse_animation() const { return pulse_animation_.get(); } + + // Returns the hover animation. This may return null. + const SlideAnimation* hover_animation() const { + return hover_animation_.get(); + } + + views::ImageButton* close_button() const { return close_button_; } + + // Paints the icon at the specified x-coordinate. + void PaintIcon(gfx::Canvas* canvas, int x, int y); + void PaintTitle(gfx::Canvas* canvas, SkColor title_color); + + // Overridden from AnimationDelegate: + virtual void AnimationProgressed(const Animation* animation); + virtual void AnimationCanceled(const Animation* animation); + virtual void AnimationEnded(const Animation* animation); + + // views::ButtonListener overrides: + virtual void ButtonPressed(views::Button* sender, + const views::Event& event); + + // views::ContextMenuController overrides: + virtual void ShowContextMenu(views::View* source, + const gfx::Point& p, + bool is_mouse_gesture); + + // views::View overrides: + virtual void ThemeChanged(); + + // Returns the bounds of the title. + virtual const gfx::Rect& title_bounds() const = 0; + + // Close button size. + static int close_button_height() { return close_button_height_; } + static int close_button_width() { return close_button_width_; } + + // Size (width/height) of the loading animation. + static int loading_animation_size() { return loading_animation_size_; } + + static gfx::Font* font() { return font_; } + static int font_height() { return font_height_; } + + private: + // The animation object used to swap the favicon with the sad tab icon. + class FavIconCrashAnimation; + + // Set the temporary offset for the favicon. This is used during the crash + // animation. + void SetFavIconHidingOffset(int offset); + + void DisplayCrashedFavIcon(); + void ResetCrashedFavIcon(); + + // Starts/Stops the crash animation. + void StartCrashAnimation(); + void StopCrashAnimation(); + + // Return true if the crash animation is currently running. + bool IsPerformingCrashAnimation() const; + + static void InitResources(); + + // Invoked when the theme changes to reload theme images. + static void LoadThemeImages(); + + // The controller. + // WARNING: this is null during detached tab dragging. + TabController* controller_; + + TabRendererData data_; + + // True if the tab is being animated closed. + bool closing_; + + // True if the tab is being dragged. + bool dragging_; + + // Pulse animation. + scoped_ptr<ThrobAnimation> pulse_animation_; + + // Hover animation. + scoped_ptr<SlideAnimation> hover_animation_; + + // Crash animation. + scoped_ptr<FavIconCrashAnimation> crash_animation_; + + scoped_refptr<AnimationContainer> animation_container_; + + views::ImageButton* close_button_; + + // The current index of the loading animation. + int loading_animation_frame_; + + // Whether to disable throbber animations. Only true if this is a nano tab + // renderer and a command line flag has been passed in to disable the + // animations. + bool throbber_disabled_; + + ThemeProvider* theme_provider_; + + // The offset used to animate the favicon location. This is used when the tab + // crashes. + int fav_icon_hiding_offset_; + + bool should_display_crashed_favicon_; + + // Size of the close button. + static int close_button_width_; + static int close_button_height_; + + // Size of the loading animation frames. + static int loading_animation_size_; + + static gfx::Font* font_; + static int font_height_; + + DISALLOW_COPY_AND_ASSIGN(BaseTab); +}; + +#endif // CHROME_BROWSER_VIEWS_TABS_BASE_TAB_H_ diff --git a/chrome/browser/views/tabs/base_tab_renderer.cc b/chrome/browser/views/tabs/base_tab_renderer.cc deleted file mode 100644 index e2c2251..0000000 --- a/chrome/browser/views/tabs/base_tab_renderer.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2010 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. - -#include "chrome/browser/views/tabs/base_tab_renderer.h" - -BaseTabRenderer::BaseTabRenderer(TabController* controller) - : controller_(controller), - closing_(false), - dragging_(false) { -} - -void BaseTabRenderer::SetData(const TabRendererData& data) { - TabRendererData old(data_); - data_ = data; - DataChanged(old); - Layout(); -} - -void BaseTabRenderer::UpdateLoadingAnimation( - TabRendererData::NetworkState state) { - if (state == data_.network_state && - state == TabRendererData::NETWORK_STATE_NONE) { - // If the network state is none and hasn't changed, do nothing. Otherwise we - // need to advance the animation frame. - return; - } - - data_.network_state = state; - AdvanceLoadingAnimation(state); -} diff --git a/chrome/browser/views/tabs/base_tab_renderer.h b/chrome/browser/views/tabs/base_tab_renderer.h deleted file mode 100644 index 12491c4..0000000 --- a/chrome/browser/views/tabs/base_tab_renderer.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2010 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_VIEWS_TABS_BASE_TAB_RENDERER_H_ -#define CHROME_BROWSER_VIEWS_TABS_BASE_TAB_RENDERER_H_ - -#include "chrome/browser/views/tabs/tab_renderer_data.h" -#include "views/view.h" - -class BaseTabRenderer; - -// Controller for tabs. -class TabController { - public: - // Selects the tab. - virtual void SelectTab(BaseTabRenderer* tab) = 0; - - // Closes the tab. - virtual void CloseTab(BaseTabRenderer* tab) = 0; - - // Shows a context menu for the tab at the specified point in screen coords. - virtual void ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p) = 0; - - // Returns true if the specified Tab is selected. - virtual bool IsTabSelected(const BaseTabRenderer* tab) const = 0; - - // Returns true if the specified Tab is pinned. - virtual bool IsTabPinned(const BaseTabRenderer* tab) const = 0; - - // Potentially starts a drag for the specified Tab. - virtual void MaybeStartDrag(BaseTabRenderer* tab, - const views::MouseEvent& event) = 0; - - // Continues dragging a Tab. - virtual void ContinueDrag(const views::MouseEvent& event) = 0; - - // Ends dragging a Tab. |canceled| is true if the drag was aborted in a way - // other than the user releasing the mouse. Returns whether the tab has been - // destroyed. - virtual bool EndDrag(bool canceled) = 0; - - protected: - virtual ~TabController() {} -}; - -// Base class for tab renderers. -class BaseTabRenderer : public views::View { - public: - explicit BaseTabRenderer(TabController* controller); - - // Sets the data this tabs displays. Invokes DataChanged for subclasses to - // update themselves appropriately. - void SetData(const TabRendererData& data); - const TabRendererData& data() const { return data_; } - - // Sets the network state. If the network state changes NetworkStateChanged is - // invoked. - virtual void UpdateLoadingAnimation(TabRendererData::NetworkState state); - - // Used to set/check whether this Tab is being animated closed. - void set_closing(bool closing) { closing_ = closing; } - bool closing() const { return closing_; } - - // See description above field. - void set_dragging(bool dragging) { dragging_ = dragging; } - bool dragging() const { return dragging_; } - - protected: - // Invoked from SetData after |data_| has been updated to the new data. - virtual void DataChanged(const TabRendererData& old) {} - - // Invoked if data_.network_state changes, or the network_state is not none. - virtual void AdvanceLoadingAnimation(TabRendererData::NetworkState state) {} - - TabController* controller() const { return controller_; } - - private: - // The controller. - // WARNING: this is null during detached tab dragging. - TabController* controller_; - - TabRendererData data_; - - // True if the tab is being animated closed. - bool closing_; - - // True if the tab is being dragged. - bool dragging_; - - DISALLOW_COPY_AND_ASSIGN(BaseTabRenderer); -}; - -#endif // CHROME_BROWSER_VIEWS_TABS_BASE_TAB_RENDERER_H_ diff --git a/chrome/browser/views/tabs/base_tab_strip.cc b/chrome/browser/views/tabs/base_tab_strip.cc index 6afa538..da59c28 100644 --- a/chrome/browser/views/tabs/base_tab_strip.cc +++ b/chrome/browser/views/tabs/base_tab_strip.cc @@ -23,14 +23,14 @@ void BaseTabStrip::UpdateLoadingAnimations() { controller_->UpdateLoadingAnimations(); } -BaseTabRenderer* BaseTabStrip::GetSelectedBaseTab() const { +BaseTab* BaseTabStrip::GetSelectedBaseTab() const { return GetBaseTabAtModelIndex(controller_->GetSelectedIndex()); } void BaseTabStrip::AddTabAt(int model_index, bool foreground, const TabRendererData& data) { - BaseTabRenderer* tab = CreateTab(); + BaseTab* tab = CreateTab(); tab->SetData(data); TabData d = { tab, gfx::Rect() }; @@ -49,7 +49,7 @@ void BaseTabStrip::AddTabAt(int model_index, void BaseTabStrip::MoveTab(int from_model_index, int to_model_index) { int from_tab_data_index = ModelIndexToTabIndex(from_model_index); - BaseTabRenderer* tab = tab_data_[from_tab_data_index].tab; + BaseTab* tab = tab_data_[from_tab_data_index].tab; tab_data_.erase(tab_data_.begin() + from_tab_data_index); TabData data = {tab, gfx::Rect()}; @@ -61,13 +61,13 @@ void BaseTabStrip::MoveTab(int from_model_index, int to_model_index) { StartMoveTabAnimation(); } -BaseTabRenderer* BaseTabStrip::GetBaseTabAtModelIndex(int model_index) const { +BaseTab* BaseTabStrip::GetBaseTabAtModelIndex(int model_index) const { return base_tab_at_tab_index(ModelIndexToTabIndex(model_index)); } -int BaseTabStrip::GetModelIndexOfBaseTab(const BaseTabRenderer* tab) const { +int BaseTabStrip::GetModelIndexOfBaseTab(const BaseTab* tab) const { for (int i = 0, model_index = 0; i < tab_count(); ++i) { - BaseTabRenderer* current_tab = base_tab_at_tab_index(i); + BaseTab* current_tab = base_tab_at_tab_index(i); if (!current_tab->closing()) { if (current_tab == tab) return model_index; @@ -101,29 +101,29 @@ bool BaseTabStrip::IsDragSessionActive() const { return drag_controller_.get() != NULL; } -void BaseTabStrip::SelectTab(BaseTabRenderer* tab) { +void BaseTabStrip::SelectTab(BaseTab* tab) { int model_index = GetModelIndexOfBaseTab(tab); if (IsValidModelIndex(model_index)) controller_->SelectTab(model_index); } -void BaseTabStrip::CloseTab(BaseTabRenderer* tab) { +void BaseTabStrip::CloseTab(BaseTab* tab) { int model_index = GetModelIndexOfBaseTab(tab); if (IsValidModelIndex(model_index)) controller_->CloseTab(model_index); } -void BaseTabStrip::ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p) { +void BaseTabStrip::ShowContextMenu(BaseTab* tab, const gfx::Point& p) { controller_->ShowContextMenu(tab, p); } -bool BaseTabStrip::IsTabSelected(const BaseTabRenderer* tab) const { +bool BaseTabStrip::IsTabSelected(const BaseTab* tab) const { int model_index = GetModelIndexOfBaseTab(tab); return IsValidModelIndex(model_index) && controller_->IsTabSelected(model_index); } -bool BaseTabStrip::IsTabPinned(const BaseTabRenderer* tab) const { +bool BaseTabStrip::IsTabPinned(const BaseTab* tab) const { if (tab->closing()) return false; @@ -132,7 +132,7 @@ bool BaseTabStrip::IsTabPinned(const BaseTabRenderer* tab) const { controller_->IsTabPinned(model_index); } -void BaseTabStrip::MaybeStartDrag(BaseTabRenderer* tab, +void BaseTabStrip::MaybeStartDrag(BaseTab* tab, const views::MouseEvent& event) { // Don't accidentally start any drag operations during animations if the // mouse is down... during an animation tabs are being resized automatically, @@ -196,7 +196,7 @@ void BaseTabStrip::OnMouseReleased(const views::MouseEvent& event, EndDrag(canceled); } -void BaseTabStrip::RemoveAndDeleteTab(BaseTabRenderer* tab) { +void BaseTabStrip::RemoveAndDeleteTab(BaseTab* tab) { int tab_data_index = TabIndexOfTab(tab); DCHECK(tab_data_index != -1); @@ -207,7 +207,7 @@ void BaseTabStrip::RemoveAndDeleteTab(BaseTabRenderer* tab) { delete tab; } -int BaseTabStrip::TabIndexOfTab(BaseTabRenderer* tab) const { +int BaseTabStrip::TabIndexOfTab(BaseTab* tab) const { for (int i = 0; i < tab_count(); ++i) { if (base_tab_at_tab_index(i) == tab) return i; diff --git a/chrome/browser/views/tabs/base_tab_strip.h b/chrome/browser/views/tabs/base_tab_strip.h index b299e72..c10a924 100644 --- a/chrome/browser/views/tabs/base_tab_strip.h +++ b/chrome/browser/views/tabs/base_tab_strip.h @@ -8,10 +8,11 @@ #include <vector> #include "base/scoped_ptr.h" -#include "chrome/browser/views/tabs/base_tab_renderer.h" +#include "chrome/browser/views/tabs/base_tab.h" +#include "chrome/browser/views/tabs/tab_controller.h" #include "views/view.h" -class BaseTabRenderer; +class BaseTab; class DraggedTabController; class TabStrip; class TabStripController; @@ -65,7 +66,7 @@ class BaseTabStrip : public views::View, virtual void StopAllHighlighting() = 0; // Returns the selected tab. - virtual BaseTabRenderer* GetSelectedBaseTab() const; + virtual BaseTab* GetSelectedBaseTab() const; // Retrieves the ideal bounds for the Tab at the specified index. const gfx::Rect& ideal_bounds(int tab_data_index) { @@ -74,7 +75,7 @@ class BaseTabStrip : public views::View, // Creates and returns a tab that can be used for dragging. Ownership passes // to the caller. - virtual BaseTabRenderer* CreateTabForDragging() = 0; + virtual BaseTab* CreateTabForDragging() = 0; // Adds a tab at the specified index. void AddTabAt(int model_index, @@ -99,16 +100,16 @@ class BaseTabStrip : public views::View, virtual void SetTabData(int model_index, const TabRendererData& data) = 0; // Returns the tab at the specified model index. - virtual BaseTabRenderer* GetBaseTabAtModelIndex(int model_index) const; + virtual BaseTab* GetBaseTabAtModelIndex(int model_index) const; // Returns the tab at the specified tab index. - BaseTabRenderer* base_tab_at_tab_index(int tab_index) const { + BaseTab* base_tab_at_tab_index(int tab_index) const { return tab_data_[tab_index].tab; } // Returns the index of the specified tab in the model coordiate system, or // -1 if tab is closing or not valid. - virtual int GetModelIndexOfBaseTab(const BaseTabRenderer* tab) const; + virtual int GetModelIndexOfBaseTab(const BaseTab* tab) const; // Gets the number of Tabs in the tab strip. // WARNING: this is the number of tabs displayed by the tabstrip, which if @@ -133,12 +134,12 @@ class BaseTabStrip : public views::View, bool IsDragSessionActive() const; // TabController overrides: - virtual void SelectTab(BaseTabRenderer* tab); - virtual void CloseTab(BaseTabRenderer* tab); - virtual void ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p); - virtual bool IsTabSelected(const BaseTabRenderer* tab) const; - virtual bool IsTabPinned(const BaseTabRenderer* tab) const; - virtual void MaybeStartDrag(BaseTabRenderer* tab, + virtual void SelectTab(BaseTab* tab); + virtual void CloseTab(BaseTab* tab); + virtual void ShowContextMenu(BaseTab* tab, const gfx::Point& p); + virtual bool IsTabSelected(const BaseTab* tab) const; + virtual bool IsTabPinned(const BaseTab* tab) const; + virtual void MaybeStartDrag(BaseTab* tab, const views::MouseEvent& event); virtual void ContinueDrag(const views::MouseEvent& event); virtual bool EndDrag(bool canceled); @@ -149,7 +150,7 @@ class BaseTabStrip : public views::View, protected: // The Tabs we contain, and their last generated "good" bounds. struct TabData { - BaseTabRenderer* tab; + BaseTab* tab; gfx::Rect ideal_bounds; }; @@ -159,7 +160,7 @@ class BaseTabStrip : public views::View, bool canceled); // Creates and returns a new tab. The caller owners the returned tab. - virtual BaseTabRenderer* CreateTab() = 0; + virtual BaseTab* CreateTab() = 0; // Invoked from |AddTabAt| after the newly created tab has been inserted. // Subclasses should either start an animation, or layout. @@ -171,7 +172,7 @@ class BaseTabStrip : public views::View, // Cleans up the Tab from the TabStrip. This is called from the tab animation // code and is not a general-purpose method. - void RemoveAndDeleteTab(BaseTabRenderer* tab); + void RemoveAndDeleteTab(BaseTab* tab); // Resets the bounds of all non-closing tabs. virtual void GenerateIdealBounds() = 0; @@ -182,7 +183,7 @@ class BaseTabStrip : public views::View, // Returns the index into |tab_data_| corresponding to the specified tab, or // -1 if the tab isn't in |tab_data_|. - int TabIndexOfTab(BaseTabRenderer* tab) const; + int TabIndexOfTab(BaseTab* tab) const; // Stops any ongoing animations. If |layout| is true and an animation is // ongoing this does a layout. @@ -192,8 +193,8 @@ class BaseTabStrip : public views::View, void DestroyDragController(); // Used by DraggedTabController when the user starts or stops dragging a tab. - virtual void StartedDraggingTab(BaseTabRenderer* tab) = 0; - virtual void StoppedDraggingTab(BaseTabRenderer* tab) = 0; + virtual void StartedDraggingTab(BaseTab* tab) = 0; + virtual void StoppedDraggingTab(BaseTab* tab) = 0; // See description above field for details. bool attaching_dragged_tab() const { return attaching_dragged_tab_; } diff --git a/chrome/browser/views/tabs/browser_tab_strip_controller.cc b/chrome/browser/views/tabs/browser_tab_strip_controller.cc index 4ebed88..6db41c7 100644 --- a/chrome/browser/views/tabs/browser_tab_strip_controller.cc +++ b/chrome/browser/views/tabs/browser_tab_strip_controller.cc @@ -32,7 +32,7 @@ static TabRendererData::NetworkState TabContentsNetworkState( class BrowserTabStripController::TabContextMenuContents : public menus::SimpleMenuModel::Delegate { public: - TabContextMenuContents(BaseTabRenderer* tab, + TabContextMenuContents(BaseTab* tab, BrowserTabStripController* controller) : ALLOW_THIS_IN_INITIALIZER_LIST( model_(this, controller->IsTabPinned(tab))), @@ -96,7 +96,7 @@ class BrowserTabStripController::TabContextMenuContents scoped_ptr<views::Menu2> menu_; // The tab we're showing a menu for. - BaseTabRenderer* tab_; + BaseTab* tab_; // A pointer back to our hosting controller, for command state information. BrowserTabStripController* controller_; @@ -140,7 +140,7 @@ void BrowserTabStripController::InitFromModel(BaseTabStrip* tabstrip) { bool BrowserTabStripController::IsCommandEnabledForTab( TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab) const { + BaseTab* tab) const { int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); return model_->ContainsIndex(model_index) ? model_->IsContextMenuCommandEnabled(model_index, command_id) : false; @@ -148,7 +148,7 @@ bool BrowserTabStripController::IsCommandEnabledForTab( bool BrowserTabStripController::IsCommandCheckedForTab( TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab) const { + BaseTab* tab) const { int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); return model_->ContainsIndex(model_index) ? model_->IsContextMenuCommandChecked(model_index, command_id) : false; @@ -156,13 +156,13 @@ bool BrowserTabStripController::IsCommandCheckedForTab( void BrowserTabStripController::ExecuteCommandForTab( TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab) { + BaseTab* tab) { int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); if (model_->ContainsIndex(model_index)) model_->ExecuteContextMenuCommand(model_index, command_id); } -bool BrowserTabStripController::IsTabPinned(BaseTabRenderer* tab) { +bool BrowserTabStripController::IsTabPinned(BaseTab* tab) { return IsTabPinned(tabstrip_->GetModelIndexOfBaseTab(tab)); } @@ -201,7 +201,7 @@ void BrowserTabStripController::CloseTab(int model_index) { model_->CloseTabContentsAt(model_index); } -void BrowserTabStripController::ShowContextMenu(BaseTabRenderer* tab, +void BrowserTabStripController::ShowContextMenu(BaseTab* tab, const gfx::Point& p) { context_menu_contents_.reset(new TabContextMenuContents(tab, this)); context_menu_contents_->RunMenuAt(p); @@ -213,7 +213,7 @@ void BrowserTabStripController::UpdateLoadingAnimations() { // be processed before us and invokes this). for (int tab_index = 0, tab_count = tabstrip_->tab_count(); tab_index < tab_count; ++tab_index) { - BaseTabRenderer* tab = tabstrip_->base_tab_at_tab_index(tab_index); + BaseTab* tab = tabstrip_->base_tab_at_tab_index(tab_index); int model_index = tabstrip_->GetModelIndexOfBaseTab(tab); if (model_->ContainsIndex(model_index)) { TabContents* contents = model_->GetTabContentsAt(model_index); @@ -372,7 +372,7 @@ void BrowserTabStripController::SetTabRendererDataFromModel( void BrowserTabStripController::StartHighlightTabsForCommand( TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab) { + BaseTab* tab) { if (command_id == TabStripModel::CommandCloseTabsOpenedBy || command_id == TabStripModel::CommandCloseOtherTabs || command_id == TabStripModel::CommandCloseTabsToRight) { @@ -390,7 +390,7 @@ void BrowserTabStripController::StartHighlightTabsForCommand( void BrowserTabStripController::StopHighlightTabsForCommand( TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab) { + BaseTab* tab) { if (command_id == TabStripModel::CommandCloseTabsOpenedBy || command_id == TabStripModel::CommandCloseTabsToRight || command_id == TabStripModel::CommandCloseOtherTabs) { diff --git a/chrome/browser/views/tabs/browser_tab_strip_controller.h b/chrome/browser/views/tabs/browser_tab_strip_controller.h index c0e3244..cff9650 100644 --- a/chrome/browser/views/tabs/browser_tab_strip_controller.h +++ b/chrome/browser/views/tabs/browser_tab_strip_controller.h @@ -9,7 +9,7 @@ #include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/views/tabs/tab_strip_controller.h" -class BaseTabRenderer; +class BaseTab; class BaseTabStrip; struct TabRendererData; @@ -27,12 +27,12 @@ class BrowserTabStripController : public TabStripController, TabStripModel* model() const { return model_; } bool IsCommandEnabledForTab(TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab) const; + BaseTab* tab) const; bool IsCommandCheckedForTab(TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab) const; + BaseTab* tab) const; void ExecuteCommandForTab(TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab); - bool IsTabPinned(BaseTabRenderer* tab); + BaseTab* tab); + bool IsTabPinned(BaseTab* tab); // TabStripController implementation: virtual int GetCount() const; @@ -43,7 +43,7 @@ class BrowserTabStripController : public TabStripController, virtual bool IsNewTabPage(int model_index) const; virtual void SelectTab(int model_index); virtual void CloseTab(int model_index); - virtual void ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p); + virtual void ShowContextMenu(BaseTab* tab, const gfx::Point& p); virtual void UpdateLoadingAnimations(); virtual int HasAvailableDragActions() const; virtual void PerformDrop(bool drop_before, int index, const GURL& url); @@ -85,10 +85,10 @@ class BrowserTabStripController : public TabStripController, void StartHighlightTabsForCommand( TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab); + BaseTab* tab); void StopHighlightTabsForCommand( TabStripModel::ContextMenuCommand command_id, - BaseTabRenderer* tab); + BaseTab* tab); Profile* profile() const { return model_->profile(); } diff --git a/chrome/browser/views/tabs/dragged_tab_controller.cc b/chrome/browser/views/tabs/dragged_tab_controller.cc index f3f2fff..a21779b 100644 --- a/chrome/browser/views/tabs/dragged_tab_controller.cc +++ b/chrome/browser/views/tabs/dragged_tab_controller.cc @@ -20,11 +20,12 @@ #include "chrome/browser/tabs/tab_strip_model.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/views/frame/browser_view.h" -#include "chrome/browser/views/tabs/base_tab_renderer.h" +#include "chrome/browser/views/tabs/base_tab.h" #include "chrome/browser/views/tabs/base_tab_strip.h" #include "chrome/browser/views/tabs/browser_tab_strip_controller.h" #include "chrome/browser/views/tabs/dragged_tab_view.h" #include "chrome/browser/views/tabs/native_view_photobooth.h" +#include "chrome/browser/views/tabs/tab.h" #include "chrome/browser/views/tabs/tab_strip.h" #include "chrome/common/notification_service.h" #include "gfx/canvas.h" @@ -312,7 +313,7 @@ class DraggedTabController::DockDisplayer : public AnimationDelegate { /////////////////////////////////////////////////////////////////////////////// // DraggedTabController, public: -DraggedTabController::DraggedTabController(BaseTabRenderer* source_tab, +DraggedTabController::DraggedTabController(BaseTab* source_tab, BaseTabStrip* source_tabstrip) : dragged_contents_(NULL), original_delegate_(NULL), @@ -801,7 +802,7 @@ void DraggedTabController::Attach(BaseTabStrip* attached_tabstrip, // And we don't need the dragged view. view_.reset(); - BaseTabRenderer* tab = GetTabMatchingDraggedContents(attached_tabstrip_); + BaseTab* tab = GetTabMatchingDraggedContents(attached_tabstrip_); if (!tab) { // There is no Tab in |attached_tabstrip| that corresponds to the dragged @@ -978,7 +979,7 @@ gfx::Point DraggedTabController::GetAttachedTabDragPoint( return gfx::Point(x, y); } -BaseTabRenderer* DraggedTabController::GetTabMatchingDraggedContents( +BaseTab* DraggedTabController::GetTabMatchingDraggedContents( BaseTabStrip* tabstrip) const { int model_index = GetModel(tabstrip)->GetIndexOfTabContents(dragged_contents_); @@ -1173,12 +1174,12 @@ void DraggedTabController::EnsureDraggedView(const TabRendererData& data) { if (!view_.get()) { gfx::Rect tab_bounds; dragged_contents_->GetContainerBounds(&tab_bounds); - BaseTabRenderer* renderer = source_tabstrip_->CreateTabForDragging(); + BaseTab* renderer = source_tabstrip_->CreateTabForDragging(); renderer->SetData(data); // DraggedTabView takes ownership of renderer. view_.reset(new DraggedTabView(renderer, mouse_offset_, tab_bounds.size(), - TabRenderer::GetMinimumSelectedSize())); + Tab::GetMinimumSelectedSize())); } } diff --git a/chrome/browser/views/tabs/dragged_tab_controller.h b/chrome/browser/views/tabs/dragged_tab_controller.h index 8e3355f..c1e62df 100644 --- a/chrome/browser/views/tabs/dragged_tab_controller.h +++ b/chrome/browser/views/tabs/dragged_tab_controller.h @@ -15,7 +15,7 @@ namespace views { class View; } -class BaseTabRenderer; +class BaseTab; class BaseTabStrip; class DraggedTabView; class NativeViewPhotobooth; @@ -38,7 +38,7 @@ class DraggedTabController : public TabContentsDelegate, public NotificationObserver, public MessageLoopForUI::Observer { public: - DraggedTabController(BaseTabRenderer* source_tab, + DraggedTabController(BaseTab* source_tab, BaseTabStrip* source_tabstrip); virtual ~DraggedTabController(); @@ -204,7 +204,7 @@ class DraggedTabController : public TabContentsDelegate, // Finds the Tab within the specified TabStrip that corresponds to the // dragged TabContents. - BaseTabRenderer* GetTabMatchingDraggedContents(BaseTabStrip* tabstrip) const; + BaseTab* GetTabMatchingDraggedContents(BaseTabStrip* tabstrip) const; // Does the work for EndDrag. If we actually started a drag and |how_end| is // not TAB_DESTROYED then one of EndDrag or RevertDrag is invoked. @@ -266,7 +266,7 @@ class DraggedTabController : public TabContentsDelegate, BaseTabStrip* attached_tabstrip_; // If attached this is the tab we're dragging. - BaseTabRenderer* attached_tab_; + BaseTab* attached_tab_; // The visual representation of the dragged Tab. scoped_ptr<DraggedTabView> view_; diff --git a/chrome/browser/views/tabs/side_tab.cc b/chrome/browser/views/tabs/side_tab.cc index c134b59..58da29d 100644 --- a/chrome/browser/views/tabs/side_tab.cc +++ b/chrome/browser/views/tabs/side_tab.cc @@ -23,101 +23,42 @@ const int kTitleCloseSpacing = 4; const SkScalar kRoundRectRadius = 5; const SkColor kTabBackgroundColor = SK_ColorWHITE; const SkAlpha kBackgroundTabAlpha = 170; -static const int kHoverDurationMs = 900; - -static SkBitmap* waiting_animation_frames = NULL; -static SkBitmap* loading_animation_frames = 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 -gfx::Font* SideTab::font_ = NULL; -SkBitmap* SideTab::close_button_n_ = NULL; -SkBitmap* SideTab::close_button_h_ = NULL; -SkBitmap* SideTab::close_button_p_ = NULL; - //////////////////////////////////////////////////////////////////////////////// // SideTab, public: SideTab::SideTab(TabController* controller) - : BaseTabRenderer(controller), - close_button_(NULL), - loading_animation_frame_(0) { - InitClass(); - - views::ImageButton* close_button = new views::ImageButton(this); - close_button->SetImage(views::CustomButton::BS_NORMAL, close_button_n_); - close_button->SetImage(views::CustomButton::BS_HOT, close_button_h_); - close_button->SetImage(views::CustomButton::BS_PUSHED, close_button_p_); - close_button_ = close_button; - AddChildView(close_button_); - - hover_animation_.reset(new SlideAnimation(this)); - hover_animation_->SetSlideDuration(kHoverDurationMs); - - SetContextMenuController(this); + : BaseTab(controller) { } SideTab::~SideTab() { } //////////////////////////////////////////////////////////////////////////////// -// SideTab, AnimationDelegate implementation: - -void SideTab::AnimationProgressed(const Animation* animation) { - SchedulePaint(); -} - -void SideTab::AnimationCanceled(const Animation* animation) { - AnimationEnded(animation); -} - -void SideTab::AnimationEnded(const Animation* animation) { - SchedulePaint(); -} - -//////////////////////////////////////////////////////////////////////////////// -// SideTab, views::ButtonListener implementation: - -void SideTab::ButtonPressed(views::Button* sender, const views::Event& event) { - DCHECK(sender == close_button_); - controller()->CloseTab(this); -} - -//////////////////////////////////////////////////////////////////////////////// -// SideTab, views::ContextMenuController implementation: - -void SideTab::ShowContextMenu(views::View* source, - const gfx::Point& p, - bool is_mouse_gesture) { - controller()->ShowContextMenu(this, p); -} - -//////////////////////////////////////////////////////////////////////////////// // SideTab, views::View overrides: void SideTab::Layout() { int icon_y; int icon_x = icon_y = (height() - kIconSize) / 2; + // TODO(sky): big mini icons. icon_bounds_.SetRect(icon_x, icon_y, kIconSize, kIconSize); - gfx::Size ps = close_button_->GetPreferredSize(); + gfx::Size ps = close_button()->GetPreferredSize(); int close_y = (height() - ps.height()) / 2; - close_button_->SetBounds( + close_button()->SetBounds( std::max(0, width() - ps.width() - close_y), close_y, ps.width(), ps.height()); - int title_y = (height() - font_->height()) / 2; + int title_y = (height() - font_height()) / 2; int title_x = icon_bounds_.right() + kIconTitleSpacing; title_bounds_.SetRect( title_x, title_y, - std::max(0, close_button_->x() - kTitleCloseSpacing - title_x), - font_->height()); + std::max(0, close_button()->x() - kTitleCloseSpacing - title_x), + font_height()); } void SideTab::Paint(gfx::Canvas* canvas) { @@ -128,19 +69,18 @@ void SideTab::Paint(gfx::Canvas* canvas) { FillTabShapePath(&tab_shape); canvas->drawPath(tab_shape, paint); - PaintIcon(canvas); - canvas->DrawStringInt(UTF16ToWideHack(data().title), *font_, - SK_ColorBLACK, title_bounds_.x(), title_bounds_.y(), - title_bounds_.width(), title_bounds_.height()); + PaintIcon(canvas, icon_bounds_.x(), icon_bounds_.y()); + PaintTitle(canvas, SK_ColorBLACK); - if (!controller()->IsTabSelected(this) && - GetThemeProvider()->ShouldUseNativeFrame()) { + if (!IsSelected() && GetThemeProvider()->ShouldUseNativeFrame()) { // Make sure un-selected tabs are somewhat transparent. SkPaint paint; SkAlpha opacity = kBackgroundTabAlpha; - if (hover_animation_->is_animating()) - opacity = static_cast<SkAlpha>(hover_animation_->GetCurrentValue() * 255); + if (hover_animation() && hover_animation()->is_animating()) { + opacity = + static_cast<SkAlpha>(hover_animation()->GetCurrentValue() * 255); + } paint.setColor(SkColorSetARGB(kBackgroundTabAlpha, 255, 255, 255)); paint.setXfermodeMode(SkXfermode::kDstIn_Mode); @@ -154,45 +94,6 @@ gfx::Size SideTab::GetPreferredSize() { return gfx::Size(0, 27); } -void SideTab::OnMouseEntered(const views::MouseEvent& event) { - hover_animation_->SetTweenType(Tween::EASE_OUT); - hover_animation_->Show(); -} - -void SideTab::OnMouseExited(const views::MouseEvent& event) { - hover_animation_->SetTweenType(Tween::EASE_IN); - hover_animation_->Hide(); -} - -bool SideTab::OnMousePressed(const views::MouseEvent& event) { - if (event.IsOnlyLeftMouseButton()) - controller()->SelectTab(this); - return true; -} - -// TODO(sky): refactor to BaseTabRenderer. -void SideTab::AdvanceLoadingAnimation(TabRendererData::NetworkState 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 (state == TabRendererData::NETWORK_STATE_WAITING && - state == TabRendererData::NETWORK_STATE_LOADING) { - loading_animation_frame_ = loading_animation_frame_count - - (loading_animation_frame_ / waiting_to_loading_frame_count_ratio); - } - - if (state != TabRendererData::NETWORK_STATE_NONE) { - loading_animation_frame_ = ++loading_animation_frame_ % - ((state == TabRendererData::NETWORK_STATE_WAITING) ? - waiting_animation_frame_count : - loading_animation_frame_count); - } else { - loading_animation_frame_ = 0; - } - SchedulePaint(); -} - //////////////////////////////////////////////////////////////////////////////// // SideTab, private: @@ -214,60 +115,3 @@ void SideTab::FillTabShapePath(gfx::Path* path) { path->lineTo(SkIntToScalar(kRoundRectRadius), 0); path->close(); } - -void SideTab::PaintIcon(gfx::Canvas* canvas) { - if (data().network_state == TabRendererData::NETWORK_STATE_NONE) { - canvas->DrawBitmapInt(data().favicon, 0, 0, kIconSize, kIconSize, - icon_bounds_.x(), icon_bounds_.y(), - icon_bounds_.width(), icon_bounds_.height(), false); - } else { - PaintLoadingAnimation(canvas); - } -} - -void SideTab::PaintLoadingAnimation(gfx::Canvas* canvas) { - SkBitmap* frames = - (data().network_state == TabRendererData::NETWORK_STATE_WAITING) ? - waiting_animation_frames : loading_animation_frames; - int image_size = frames->height(); - int image_offset = loading_animation_frame_ * image_size; - int dst_y = (height() - image_size) / 2; - canvas->DrawBitmapInt(*frames, image_offset, 0, image_size, - image_size, icon_bounds_.x(), dst_y, image_size, - image_size, false); -} - -// static -void SideTab::InitClass() { - static bool initialized = false; - if (!initialized) { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - font_ = new gfx::Font(rb.GetFont(ResourceBundle::BaseFont)); - - 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); - - // 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; - - initialized = true; - } -} - diff --git a/chrome/browser/views/tabs/side_tab.h b/chrome/browser/views/tabs/side_tab.h index 79fbac8..48918e2 100644 --- a/chrome/browser/views/tabs/side_tab.h +++ b/chrome/browser/views/tabs/side_tab.h @@ -6,74 +6,31 @@ #define CHROME_BROWSER_VIEWS_TABS_SIDE_TAB_H_ #include "app/slide_animation.h" -#include "chrome/browser/views/tabs/base_tab_renderer.h" +#include "chrome/browser/views/tabs/base_tab.h" #include "gfx/font.h" -#include "views/controls/button/button.h" -#include "views/view.h" class SideTab; class TabStripController; -class SideTab : public BaseTabRenderer, - public views::ContextMenuController, - public views::ButtonListener, - public AnimationDelegate { +class SideTab : public BaseTab { public: explicit SideTab(TabController* controller); virtual ~SideTab(); - // AnimationDelegate implementation: - virtual void AnimationProgressed(const Animation* animation); - virtual void AnimationCanceled(const Animation* animation); - virtual void AnimationEnded(const Animation* animation); - - // views::ButtonListener implementation: - virtual void ButtonPressed(views::Button* sender, const views::Event& event); - - // views::ContextMenuController implementation: - virtual void ShowContextMenu(views::View* source, - const gfx::Point& p, - bool is_mouse_gesture); - // views::View Overrides: virtual void Layout(); virtual void Paint(gfx::Canvas* canvas); virtual gfx::Size GetPreferredSize(); - virtual void OnMouseEntered(const views::MouseEvent& event); - virtual void OnMouseExited(const views::MouseEvent& event); - virtual bool OnMousePressed(const views::MouseEvent& event); protected: - // BaseTabRenderer overrides. - virtual void AdvanceLoadingAnimation(TabRendererData::NetworkState state); + virtual const gfx::Rect& title_bounds() const { return title_bounds_; } private: void FillTabShapePath(gfx::Path* path); - // Paint various components of the tab. - void PaintIcon(gfx::Canvas* canvas); - void PaintLoadingAnimation(gfx::Canvas* canvas); - - // Loads class-specific resources. - static void InitClass(); - gfx::Rect icon_bounds_; gfx::Rect title_bounds_; - views::Button* close_button_; - - // Hover animation. - scoped_ptr<SlideAnimation> hover_animation_; - - static gfx::Font* font_; - static SkBitmap* close_button_n_; - static SkBitmap* close_button_m_; - static SkBitmap* close_button_h_; - static SkBitmap* close_button_p_; - - // The current index into the Animation image strip. - int loading_animation_frame_; - DISALLOW_COPY_AND_ASSIGN(SideTab); }; diff --git a/chrome/browser/views/tabs/side_tab_strip.cc b/chrome/browser/views/tabs/side_tab_strip.cc index 2a973d5..216d103 100644 --- a/chrome/browser/views/tabs/side_tab_strip.cc +++ b/chrome/browser/views/tabs/side_tab_strip.cc @@ -57,30 +57,28 @@ void SideTabStrip::StartHighlight(int model_index) { void SideTabStrip::StopAllHighlighting() { } -BaseTabRenderer* SideTabStrip::GetBaseTabAtModelIndex(int model_index) const { - return static_cast<BaseTabRenderer*>(GetChildViewAt(model_index)); +BaseTab* SideTabStrip::CreateTabForDragging() { + SideTab* tab = new SideTab(NULL); + // Make sure the dragged tab shares our theme provider. We need to explicitly + // do this as during dragging there isn't a theme provider. + tab->set_theme_provider(GetThemeProvider()); + return tab; } -BaseTabRenderer* SideTabStrip::CreateTabForDragging() { - return new SideTab(NULL); -} - -void SideTabStrip::RemoveTabAt(int index, bool initiated_close) { - View* v = GetChildViewAt(index); - RemoveChildView(v); - delete v; +void SideTabStrip::RemoveTabAt(int model_index, bool initiated_close) { + RemoveAndDeleteTab(GetBaseTabAtModelIndex(model_index)); Layout(); } void SideTabStrip::SelectTabAt(int old_model_index, int new_model_index) { - GetChildViewAt(new_model_index)->SchedulePaint(); + GetBaseTabAtModelIndex(new_model_index)->SchedulePaint(); } void SideTabStrip::TabTitleChangedNotLoading(int model_index) { } void SideTabStrip::SetTabData(int model_index, const TabRendererData& data) { - BaseTabRenderer* tab = GetBaseTabAtModelIndex(model_index); + BaseTab* tab = GetBaseTabAtModelIndex(model_index); tab->SetData(data); tab->SchedulePaint(); } @@ -89,7 +87,7 @@ gfx::Size SideTabStrip::GetPreferredSize() { return gfx::Size(kTabStripWidth, 0); } -BaseTabRenderer* SideTabStrip::CreateTab() { +BaseTab* SideTabStrip::CreateTab() { return new SideTab(this); } @@ -99,7 +97,7 @@ void SideTabStrip::GenerateIdealBounds() { int y = layout_rect.y(); for (int i = 0; i < tab_count(); ++i) { - BaseTabRenderer* tab = base_tab_at_tab_index(i); + BaseTab* tab = base_tab_at_tab_index(i); if (!tab->closing()) { gfx::Rect bounds = gfx::Rect(layout_rect.x(), y, layout_rect.width(), tab->GetPreferredSize().height()); diff --git a/chrome/browser/views/tabs/side_tab_strip.h b/chrome/browser/views/tabs/side_tab_strip.h index 67f9e82..fb23a4f 100644 --- a/chrome/browser/views/tabs/side_tab_strip.h +++ b/chrome/browser/views/tabs/side_tab_strip.h @@ -25,8 +25,7 @@ class SideTabStrip : public BaseTabStrip { virtual void StartHighlight(int model_index); virtual void StopAllHighlighting(); - virtual BaseTabRenderer* GetBaseTabAtModelIndex(int model_index) const; - virtual BaseTabRenderer* CreateTabForDragging(); + virtual BaseTab* CreateTabForDragging(); virtual void RemoveTabAt(int model_index, bool initiated_close); virtual void SelectTabAt(int old_model_index, int new_model_index); virtual void TabTitleChangedNotLoading(int model_index); @@ -37,13 +36,13 @@ class SideTabStrip : public BaseTabStrip { protected: // BaseTabStrip overrides: - virtual BaseTabRenderer* CreateTab(); + virtual BaseTab* CreateTab(); virtual void GenerateIdealBounds(); virtual void StartInsertTabAnimation(int model_index, bool foreground); virtual void StartMoveTabAnimation(); virtual void StopAnimating(bool layout); - virtual void StartedDraggingTab(BaseTabRenderer* tab) {} - virtual void StoppedDraggingTab(BaseTabRenderer* tab) {} + virtual void StartedDraggingTab(BaseTab* tab) {} + virtual void StoppedDraggingTab(BaseTab* tab) {} private: DISALLOW_COPY_AND_ASSIGN(SideTabStrip); diff --git a/chrome/browser/views/tabs/tab.cc b/chrome/browser/views/tabs/tab.cc index 9920f69..7c91604 100644 --- a/chrome/browser/views/tabs/tab.cc +++ b/chrome/browser/views/tabs/tab.cc @@ -4,143 +4,348 @@ #include "chrome/browser/views/tabs/tab.h" -#include "app/l10n_util.h" -#include "app/menus/simple_menu_model.h" +#include <limits> + +#include "app/multi_animation.h" #include "app/resource_bundle.h" -#include "base/compiler_specific.h" -#include "chrome/browser/tab_menu_model.h" -#include "chrome/browser/views/frame/browser_view.h" -#include "chrome/browser/views/tabs/tab_strip.h" +#include "app/slide_animation.h" +#include "app/throb_animation.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/browser_theme_provider.h" +#include "chrome/browser/defaults.h" #include "gfx/canvas.h" +#include "gfx/favicon_size.h" #include "gfx/font.h" #include "gfx/path.h" -#include "gfx/size.h" +#include "gfx/skbitmap_operations.h" +#include "grit/app_resources.h" #include "grit/generated_resources.h" -#include "views/controls/menu/menu_2.h" +#include "grit/theme_resources.h" +#include "third_party/skia/include/effects/SkGradientShader.h" +#include "views/controls/button/image_button.h" #include "views/widget/tooltip_manager.h" #include "views/widget/widget.h" - -const std::string Tab::kTabClassName = "browser/tabs/Tab"; - +#include "views/window/non_client_view.h" +#include "views/window/window.h" + +static const int kLeftPadding = 16; +static const int kTopPadding = 6; +static const int kRightPadding = 15; +static const int kBottomPadding = 5; +static const int kDropShadowHeight = 2; +static const int kToolbarOverlap = 1; +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; + +// Vertical adjustment to the favicon when the tab has a large icon. +static const int kAppTapFaviconVerticalAdjustment = 2; + +// When a non-mini-tab becomes a mini-tab the width of the tab animates. If +// the width of a mini-tab is >= kMiniTabRendererAsNormalTabWidth then the tab +// is rendered as a normal tab. This is done to avoid having the title +// immediately disappear when transitioning a tab from normal to mini-tab. +static const int kMiniTabRendererAsNormalTabWidth = + browser_defaults::kMiniTabWidth + 30; + +// How opaque to make the hover state (out of 1). +static const double kHoverOpacity = 0.33; + +// Used when |render_as_new_tab| is true. +static SkBitmap* new_tab_mask = NULL; +static SkBitmap* new_tab_shadow = NULL; + +Tab::TabImage Tab::tab_alpha = {0}; +Tab::TabImage Tab::tab_active = {0}; +Tab::TabImage Tab::tab_active_nano = {0}; +Tab::TabImage Tab::tab_inactive = {0}; +Tab::TabImage Tab::tab_inactive_nano = {0}; +Tab::TabImage Tab::tab_alpha_nano = {0}; + +// Durations for the various parts of the mini tab title animation. +static const int kMiniTitleChangeAnimationDuration1MS = 1000; +static const int kMiniTitleChangeAnimationDuration2MS = 500; +static const int kMiniTitleChangeAnimationDuration3MS = 800; + +// Offset from the right edge for the start of the mini title change animation. +static const int kMiniTitleChangeInitialXOffset = 6; + +// Radius of the radial gradient used for mini title change animation. +static const int kMiniTitleChangeGradientRadius = 20; + +// Colors of the gradient used during the mini title change animation. +static const SkColor kMiniTitleChangeGradientColor1 = SK_ColorWHITE; +static const SkColor kMiniTitleChangeGradientColor2 = + SkColorSetARGB(0, 255, 255, 255); + +// Hit mask constants. static const SkScalar kTabCapWidth = 15; static const SkScalar kTabTopCurveWidth = 4; static const SkScalar kTabBottomCurveWidth = 3; -/////////////////////////////////////////////////////////////////////////////// +namespace { + +void InitTabResources() { + static bool initialized = false; + if (initialized) + return; + + initialized = true; + Tab::LoadTabImages(); +} + +} // namespace + +// static +const char Tab::kViewClassName[] = "browser/tabs/Tab"; + +//////////////////////////////////////////////////////////////////////////////// // Tab, public: Tab::Tab(TabController* controller) - : TabRenderer(controller) { - close_button()->SetTooltipText(l10n_util::GetString(IDS_TOOLTIP_CLOSE_TAB)); - close_button()->SetAccessibleName(l10n_util::GetString(IDS_ACCNAME_CLOSE)); - close_button()->SetAnimationDuration(0); - SetContextMenuController(this); + : BaseTab(controller), + showing_icon_(false), + showing_close_button_(false), + close_button_color_(NULL), + render_as_new_tab_(false), + render_unselected_(false), + alpha_(1) { + InitTabResources(); } Tab::~Tab() { } -/////////////////////////////////////////////////////////////////////////////// -// Tab, TabRenderer overrides: +void Tab::SizeToNewTabButtonImages() { + SetBounds(x(), y(), new_tab_shadow->width(), new_tab_shadow->height()); +} -bool Tab::IsSelected() const { - return controller() ? controller()->IsTabSelected(this) : true; +void Tab::StartMiniTabTitleAnimation() { + if (!mini_title_animation_.get()) { + MultiAnimation::Parts parts; + parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration1MS, + Tween::EASE_OUT)); + parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration2MS, + Tween::ZERO)); + parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration3MS, + Tween::EASE_IN)); + mini_title_animation_.reset(new MultiAnimation(parts)); + mini_title_animation_->SetContainer(animation_container()); + mini_title_animation_->set_delegate(this); + } + mini_title_animation_->Start(); } -/////////////////////////////////////////////////////////////////////////////// -// Tab, views::View overrides: +void Tab::StopMiniTabTitleAnimation() { + if (mini_title_animation_.get()) + mini_title_animation_->Stop(); +} -bool Tab::HasHitTestMask() const { - return true; +void Tab::PaintIcon(gfx::Canvas* canvas) { + BaseTab::PaintIcon(canvas, favicon_bounds_.x(), favicon_bounds_.y()); } -void Tab::GetHitTestMask(gfx::Path* mask) const { - MakePathForTab(mask); +// static +gfx::Size Tab::GetMinimumUnselectedSize() { + InitTabResources(); + + 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.image_l->height()); + return minimum_size; } -bool Tab::OnMousePressed(const views::MouseEvent& event) { - if (event.IsOnlyLeftMouseButton()) { - // Store whether or not we were selected just now... we only want to be - // able to drag foreground tabs, so we don't start dragging the tab if - // it was in the background. - bool just_selected = !IsSelected(); - if (just_selected) { - controller()->SelectTab(this); - } - controller()->MaybeStartDrag(this, event); - } - return true; +// static +gfx::Size Tab::GetMinimumSelectedSize() { + gfx::Size minimum_size = GetMinimumUnselectedSize(); + minimum_size.set_width(kLeftPadding + kFavIconSize + kRightPadding); + return minimum_size; } -bool Tab::OnMouseDragged(const views::MouseEvent& event) { - controller()->ContinueDrag(event); - return true; +// static +gfx::Size Tab::GetStandardSize() { + gfx::Size standard_size = GetMinimumUnselectedSize(); + standard_size.set_width( + standard_size.width() + kFavIconTitleSpacing + kStandardTitleWidth); + return standard_size; } -void Tab::OnMouseReleased(const views::MouseEvent& event, bool canceled) { - // Notify the drag helper that we're done with any potential drag operations. - // Clean up the drag helper, which is re-created on the next mouse press. - // In some cases, ending the drag will schedule the tab for destruction; if - // so, bail immediately, since our members are already dead and we shouldn't - // do anything else except drop the tab where it is. - if (controller()->EndDrag(canceled)) - return; +// static +int Tab::GetMiniWidth() { + return browser_defaults::kMiniTabWidth; +} - // Close tab on middle click, but only if the button is released over the tab - // (normal windows behavior is to discard presses of a UI element where the - // releases happen off the element). - if (event.IsMiddleMouseButton() && HitTest(event.location())) - controller()->CloseTab(this); -} - -bool Tab::GetTooltipText(const gfx::Point& p, std::wstring* tooltip) { - std::wstring title = GetTitle(); - if (!title.empty()) { - // Only show the tooltip if the title is truncated. - gfx::Font font; - if (font.GetStringWidth(title) > title_bounds().width()) { - *tooltip = title; - return true; - } +//////////////////////////////////////////////////////////////////////////////// +// Tab, protected: + +void Tab::DataChanged(const TabRendererData& old) { + if (data().phantom) + StopMiniTabTitleAnimation(); + + if (data().blocked != old.blocked) { + if (data().blocked) + StartPulse(); + else + StopPulse(); } - return false; } -bool Tab::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* origin) { - gfx::Font font; - origin->set_x(title_bounds().x() + 10); - origin->set_y(-views::TooltipManager::GetTooltipHeight() - 4); - return true; -} +//////////////////////////////////////////////////////////////////////////////// +// Tab, views::View overrides: -bool Tab::GetAccessibleRole(AccessibilityTypes::Role* role) { - DCHECK(role); +void Tab::Paint(gfx::Canvas* canvas) { + if (render_as_new_tab_) { + if (base::i18n::IsRTL()) { + canvas->TranslateInt(width(), 0); + canvas->ScaleInt(-1, 1); + } + PaintAsNewTab(canvas); + return; + } - *role = AccessibilityTypes::ROLE_PAGETAB; - return true; + // Don't paint if we're narrower than we can render correctly. (This should + // only happen during animations). + if (width() < GetMinimumUnselectedSize().width() && !data().mini) + return; + + // See if the model changes whether the icons should be painted. + const bool show_icon = ShouldShowIcon() && !data().phantom; + const bool show_close_button = ShouldShowCloseBox(); + if (show_icon != showing_icon_ || + show_close_button != showing_close_button_) + Layout(); + + PaintTabBackground(canvas); + + SkColor title_color = GetThemeProvider()-> + GetColor(IsSelected() ? + BrowserThemeProvider::COLOR_TAB_TEXT : + BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT); + + if (!data().mini || width() > kMiniTabRendererAsNormalTabWidth) + PaintTitle(canvas, title_color); + + if (show_icon) + PaintIcon(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; + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + close_button()->SetBackground(close_button_color_, + rb.GetBitmapNamed(IDR_TAB_CLOSE), + rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK)); + } } -/////////////////////////////////////////////////////////////////////////////// -// Tab, views::ContextMenuController implementation: +void Tab::Layout() { + gfx::Rect lb = GetLocalBounds(false); + if (lb.IsEmpty()) + return; + lb.Inset(kLeftPadding, kTopPadding, kRightPadding, kBottomPadding); + + // 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, font_height()); + content_height = std::max(content_height, close_button_height()); + + // Size the Favicon. + showing_icon_ = ShouldShowIcon(); + if (showing_icon_) { + // Use the size of the favicon as apps use a bigger favicon size. + int favicon_size = + !data().favicon.empty() ? data().favicon.width() : kFavIconSize; + int favicon_top = kTopPadding + content_height / 2 - favicon_size / 2; + int favicon_left = lb.x(); + if (favicon_size != kFavIconSize) { + favicon_left -= (favicon_size - kFavIconSize) / 2; + favicon_top -= kAppTapFaviconVerticalAdjustment; + } + favicon_bounds_.SetRect(favicon_left, favicon_top, + favicon_size, favicon_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() - favicon_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); + } + } + } else { + favicon_bounds_.SetRect(lb.x(), lb.y(), 0, 0); + } -void Tab::ShowContextMenu(views::View* source, - const gfx::Point& p, - bool is_mouse_gesture) { - controller()->ShowContextMenu(this, p); -} + // 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); + } + + int title_left = favicon_bounds_.right() + kFavIconTitleSpacing; + int title_top = kTopPadding + (content_height - font_height()) / 2; + // Size the Title text to fill the remaining space. + if (!data().mini || width() >= kMiniTabRendererAsNormalTabWidth) { + // 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 = GetMinimumUnselectedSize(); + int text_height = title_top + 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()->x() - + kTitleCloseButtonSpacing - title_left, 0); + } else { + title_width = std::max(lb.width() - title_left, 0); + } + title_bounds_.SetRect(title_left, title_top, title_width, font_height()); + } else { + title_bounds_.SetRect(title_left, title_top, 0, 0); + } -/////////////////////////////////////////////////////////////////////////////// -// views::ButtonListener implementation: + // Certain UI elements within the Tab (the favicon, 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 views::Button + // instance) are automatically mirrored by the mirroring infrastructure in + // views. 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_)); +} -void Tab::ButtonPressed(views::Button* sender, const views::Event& event) { - if (sender == close_button()) - controller()->CloseTab(this); +void Tab::ThemeChanged() { + BaseTab::ThemeChanged(); + Tab::LoadTabImages(); } -/////////////////////////////////////////////////////////////////////////////// -// Tab, private: +bool Tab::HasHitTestMask() const { + return true; +} -void Tab::MakePathForTab(gfx::Path* path) const { +void Tab::GetHitTestMask(gfx::Path* path) const { DCHECK(path); SkScalar h = SkIntToScalar(height()); @@ -165,3 +370,334 @@ void Tab::MakePathForTab(gfx::Path* path) const { path->lineTo(0, h); path->close(); } + +bool Tab::GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* origin) { + origin->set_x(title_bounds().x() + 10); + origin->set_y(-views::TooltipManager::GetTooltipHeight() - 4); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Tab, private + +void Tab::PaintTabBackground(gfx::Canvas* canvas) { + if (IsSelected()) { + PaintActiveTabBackground(canvas); + } else { + if (mini_title_animation_.get() && mini_title_animation_->is_animating()) + PaintInactiveTabBackgroundWithTitleChange(canvas); + else + PaintInactiveTabBackground(canvas); + + double throb_value = GetThrobValue(); + if (throb_value > 0) { + SkRect bounds; + bounds.set(0, 0, SkIntToScalar(width()), SkIntToScalar(height())); + canvas->saveLayerAlpha(&bounds, static_cast<int>(throb_value * 0xff), + SkCanvas::kARGB_ClipLayer_SaveFlag); + canvas->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode); + PaintActiveTabBackground(canvas); + canvas->restore(); + } + } +} + +void Tab::PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas* canvas) { + // Render the inactive tab background. We'll use this for clipping. + gfx::Canvas background_canvas(width(), height(), false); + PaintInactiveTabBackground(&background_canvas); + + SkBitmap background_image = background_canvas.ExtractBitmap(); + + // Draw a radial gradient to hover_canvas. + gfx::Canvas hover_canvas(width(), height(), false); + int radius = kMiniTitleChangeGradientRadius; + int x0 = width() + radius - kMiniTitleChangeInitialXOffset; + int x1 = radius; + int x2 = -radius; + int x; + if (mini_title_animation_->current_part_index() == 0) { + x = mini_title_animation_->CurrentValueBetween(x0, x1); + } else if (mini_title_animation_->current_part_index() == 1) { + x = x1; + } else { + x = mini_title_animation_->CurrentValueBetween(x1, x2); + } + SkPaint paint; + SkPoint loc = { SkIntToScalar(x), SkIntToScalar(0) }; + SkColor colors[2]; + colors[0] = kMiniTitleChangeGradientColor1; + colors[1] = kMiniTitleChangeGradientColor2; + SkShader* shader = SkGradientShader::CreateRadial( + loc, + SkIntToScalar(radius), + colors, + NULL, + 2, + SkShader::kClamp_TileMode); + paint.setShader(shader); + shader->unref(); + hover_canvas.FillRectInt(x - radius, -radius, radius * 2, radius * 2, paint); + + // Draw the radial gradient clipped to the background into hover_image. + SkBitmap hover_image = SkBitmapOperations::CreateMaskedBitmap( + hover_canvas.ExtractBitmap(), background_image); + + // Draw the tab background to the canvas. + canvas->DrawBitmapInt(background_image, 0, 0); + + // And then the gradient on top of that. + if (mini_title_animation_->current_part_index() == 2) { + canvas->saveLayerAlpha(NULL, + mini_title_animation_->CurrentValueBetween(255, 0)); + canvas->DrawBitmapInt(hover_image, 0, 0); + canvas->restore(); + } else { + canvas->DrawBitmapInt(hover_image, 0, 0); + } +} + +void Tab::PaintInactiveTabBackground(gfx::Canvas* canvas) { + bool is_otr = data().off_the_record; + + // The tab image needs to be lined up with the background image + // so that it feels partially transparent. These offsets represent the tab + // position within the frame background image. + int offset = GetX(views::View::APPLY_MIRRORING_TRANSFORMATION) + + background_offset_.x(); + + int tab_id; + if (GetWidget() && + GetWidget()->GetWindow()->GetNonClientView()->UseNativeFrame()) { + tab_id = IDR_THEME_TAB_BACKGROUND_V; + } else { + tab_id = is_otr ? IDR_THEME_TAB_BACKGROUND_INCOGNITO : + IDR_THEME_TAB_BACKGROUND; + } + + SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(tab_id); + + // App tabs are drawn slightly differently (as nano tabs). + TabImage* tab_image = data().app ? &tab_active_nano : &tab_active; + TabImage* tab_inactive_image = data().app ? &tab_inactive_nano : + &tab_inactive; + TabImage* alpha = data().app ? &tab_alpha_nano : &tab_alpha; + + // If the theme is providing a custom background image, then its top edge + // should be at the top of the tab. Otherwise, we assume that the background + // image is a composited foreground + frame image. + int bg_offset_y = GetThemeProvider()->HasCustomImage(tab_id) ? + 0 : background_offset_.y(); + + // Draw left edge. Don't draw over the toolbar, as we're not the foreground + // tab. + SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap( + *tab_bg, offset, bg_offset_y, tab_image->l_width, height()); + SkBitmap theme_l = + SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l); + canvas->DrawBitmapInt(theme_l, + 0, 0, theme_l.width(), theme_l.height() - kToolbarOverlap, + 0, 0, theme_l.width(), theme_l.height() - kToolbarOverlap, + false); + + // Draw right edge. Again, don't draw over the toolbar. + SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg, + offset + width() - tab_image->r_width, bg_offset_y, + tab_image->r_width, height()); + SkBitmap theme_r = + SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r); + canvas->DrawBitmapInt(theme_r, + 0, 0, theme_r.width(), theme_r.height() - kToolbarOverlap, + width() - theme_r.width(), 0, theme_r.width(), + theme_r.height() - kToolbarOverlap, false); + + // Draw center. Instead of masking out the top portion we simply skip over + // it by incrementing by kDropShadowHeight, since it's a simple rectangle. + // And again, don't draw over the toolbar. + canvas->TileImageInt(*tab_bg, + offset + tab_image->l_width, + bg_offset_y + kDropShadowHeight + tab_image->y_offset, + tab_image->l_width, + kDropShadowHeight + tab_image->y_offset, + width() - tab_image->l_width - tab_image->r_width, + height() - kDropShadowHeight - kToolbarOverlap - tab_image->y_offset); + + // Now draw the highlights/shadows around the tab edge. + canvas->DrawBitmapInt(*tab_inactive_image->image_l, 0, 0); + canvas->TileImageInt(*tab_inactive_image->image_c, + tab_inactive_image->l_width, 0, + width() - tab_inactive_image->l_width - + tab_inactive_image->r_width, + height()); + canvas->DrawBitmapInt(*tab_inactive_image->image_r, + width() - tab_inactive_image->r_width, 0); +} + +void Tab::PaintActiveTabBackground(gfx::Canvas* canvas) { + int offset = GetX(views::View::APPLY_MIRRORING_TRANSFORMATION) + + background_offset_.x(); + ThemeProvider* tp = GetThemeProvider(); + if (!tp) + NOTREACHED() << "Unable to get theme provider"; + + SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(IDR_THEME_TOOLBAR); + + // App tabs are drawn slightly differently (as nano tabs). + TabImage* tab_image = data().app ? &tab_active_nano : &tab_active; + TabImage* alpha = data().app ? &tab_alpha_nano : &tab_alpha; + + // Draw left edge. + SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap( + *tab_bg, offset, 0, tab_image->l_width, height()); + SkBitmap theme_l = + SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l); + canvas->DrawBitmapInt(theme_l, 0, 0); + + // Draw right edge. + SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg, + offset + width() - tab_image->r_width, 0, tab_image->r_width, height()); + SkBitmap theme_r = + SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r); + canvas->DrawBitmapInt(theme_r, width() - tab_image->r_width, 0); + + // Draw center. Instead of masking out the top portion we simply skip over it + // by incrementing by kDropShadowHeight, since it's a simple rectangle. + canvas->TileImageInt(*tab_bg, + offset + tab_image->l_width, + kDropShadowHeight + tab_image->y_offset, + tab_image->l_width, + kDropShadowHeight + tab_image->y_offset, + width() - tab_image->l_width - tab_image->r_width, + height() - kDropShadowHeight - tab_image->y_offset); + + // Now draw the highlights/shadows around the tab edge. + canvas->DrawBitmapInt(*tab_image->image_l, 0, 0); + canvas->TileImageInt(*tab_image->image_c, tab_image->l_width, 0, + width() - tab_image->l_width - tab_image->r_width, height()); + canvas->DrawBitmapInt(*tab_image->image_r, width() - tab_image->r_width, 0); +} + +void Tab::PaintAsNewTab(gfx::Canvas* canvas) { + bool is_otr = data().off_the_record; + + // The tab image needs to be lined up with the background image + // so that it feels partially transparent. These offsets represent the tab + // position within the frame background image. + int offset = GetX(views::View::APPLY_MIRRORING_TRANSFORMATION) + + background_offset_.x(); + + int tab_id; + if (GetWidget() && + GetWidget()->GetWindow()->GetNonClientView()->UseNativeFrame()) { + tab_id = IDR_THEME_TAB_BACKGROUND_V; + } else { + tab_id = is_otr ? IDR_THEME_TAB_BACKGROUND_INCOGNITO : + IDR_THEME_TAB_BACKGROUND; + } + + SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(tab_id); + + // If the theme is providing a custom background image, then its top edge + // should be at the top of the tab. Otherwise, we assume that the background + // image is a composited foreground + frame image. + int bg_offset_y = GetThemeProvider()->HasCustomImage(tab_id) ? + 0 : background_offset_.y(); + + SkBitmap image = SkBitmapOperations::CreateTiledBitmap( + *tab_bg, offset, bg_offset_y, new_tab_mask->width(), + new_tab_mask->height()); + image = SkBitmapOperations::CreateMaskedBitmap(image, *new_tab_mask); + canvas->DrawBitmapInt(image, + 0, 0, image.width(), image.height(), + 0, 0, image.width(), image.height(), + false); + + canvas->DrawBitmapInt(*new_tab_shadow, + 0, 0, new_tab_shadow->width(), new_tab_shadow->height(), + 0, 0, new_tab_shadow->width(), new_tab_shadow->height(), + false); +} + +int Tab::IconCapacity() const { + if (height() < GetMinimumUnselectedSize().height()) + return 0; + return (width() - kLeftPadding - kRightPadding) / kFavIconSize; +} + +bool Tab::ShouldShowIcon() const { + if (data().mini && height() >= GetMinimumUnselectedSize().height()) + return true; + 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 Tab::ShouldShowCloseBox() const { + // The selected tab never clips close button. + return !data().mini && (IsSelected() || IconCapacity() >= 3); +} + +double Tab::GetThrobValue() { + if (alpha_ != 1) + return alpha_; + + if (pulse_animation() && pulse_animation()->is_animating()) + return pulse_animation()->GetCurrentValue() * kHoverOpacity; + + return hover_animation() ? + kHoverOpacity * hover_animation()->GetCurrentValue() : 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// Tab, private: + +// static +void Tab::LoadTabImages() { + // We're not letting people override tab images just yet. + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + + tab_alpha.image_l = rb.GetBitmapNamed(IDR_TAB_ALPHA_LEFT); + tab_alpha.image_r = rb.GetBitmapNamed(IDR_TAB_ALPHA_RIGHT); + + tab_alpha_nano.image_l = rb.GetBitmapNamed(IDR_TAB_ALPHA_NANO_LEFT); + tab_alpha_nano.image_r = rb.GetBitmapNamed(IDR_TAB_ALPHA_NANO_RIGHT); + + tab_active.image_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_LEFT); + tab_active.image_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_CENTER); + tab_active.image_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_RIGHT); + tab_active.l_width = tab_active.image_l->width(); + tab_active.r_width = tab_active.image_r->width(); + + // The regular tab is much taller *visually* than the nano tabs. + // The images are the same height, this is really just the difference + // in whitespace above the tab image (regular vs nano). + const int kNanoTabDiffHeight = 13; + + tab_active_nano.image_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_NANO_LEFT); + tab_active_nano.image_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_NANO_CENTER); + tab_active_nano.image_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_NANO_RIGHT); + tab_active_nano.l_width = tab_active_nano.image_l->width(); + tab_active_nano.r_width = tab_active_nano.image_r->width(); + tab_active_nano.y_offset = kNanoTabDiffHeight; + + tab_inactive.image_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT); + tab_inactive.image_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER); + tab_inactive.image_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT); + tab_inactive.l_width = tab_inactive.image_l->width(); + tab_inactive.r_width = tab_inactive.image_r->width(); + + tab_inactive_nano.image_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_NANO_LEFT); + tab_inactive_nano.image_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_NANO_CENTER); + tab_inactive_nano.image_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_NANO_RIGHT); + tab_inactive_nano.l_width = tab_inactive_nano.image_l->width(); + tab_inactive_nano.r_width = tab_inactive_nano.image_r->width(); + tab_inactive_nano.y_offset = kNanoTabDiffHeight; + + new_tab_mask = rb.GetBitmapNamed(IDR_TAB_ALPHA_NEW_TAB); + new_tab_shadow = rb.GetBitmapNamed(IDR_TAB_NEW_TAB_SHADOW); +} diff --git a/chrome/browser/views/tabs/tab.h b/chrome/browser/views/tabs/tab.h index f798618..fdd550c 100644 --- a/chrome/browser/views/tabs/tab.h +++ b/chrome/browser/views/tabs/tab.h @@ -5,56 +5,164 @@ #ifndef CHROME_BROWSER_VIEWS_TABS_TAB_H_ #define CHROME_BROWSER_VIEWS_TABS_TAB_H_ -#include "chrome/browser/tabs/tab_strip_model.h" -#include "chrome/browser/views/tabs/tab_renderer.h" +#include <string> -namespace gfx { -class Path; -class Point; -} +#include "base/scoped_ptr.h" +#include "chrome/browser/views/tabs/base_tab.h" +#include "gfx/point.h" + +class MultiAnimation; +class SlideAnimation; /////////////////////////////////////////////////////////////////////////////// // -// Tab +// TabRenderer // -// A subclass of TabRenderer that represents an individual Tab in a TabStrip. +// A View that renders a Tab, either in a TabStrip or in a DraggedTabView. // /////////////////////////////////////////////////////////////////////////////// -class Tab : public TabRenderer, - public views::ContextMenuController { +class Tab : public BaseTab { public: - static const std::string kTabClassName; + // The menu button's class name. + static const char kViewClassName[]; explicit Tab(TabController* controller); virtual ~Tab(); - // TabRenderer overrides: - virtual bool IsSelected() const; + // Sizes the renderer to the size of the new tab images. This is used + // during the new tab animation. See TabStrip's description of AnimationType + // for details. + void SizeToNewTabButtonImages(); + + // Used during new tab animation to force the tab to render a new tab like + // animation. + void set_render_as_new_tab(bool value) { render_as_new_tab_ = value; } + + // Sets the alpha value to render the tab at. This is used during the new + // tab animation. + void set_alpha(double value) { alpha_ = value; } + + // Forces the tab to render unselected even though it is selected. + void set_render_unselected(bool value) { render_unselected_ = value; } + bool render_unselected() const { return render_unselected_; } + + // Start/stop the mini-tab title animation. + void StartMiniTabTitleAnimation(); + void StopMiniTabTitleAnimation(); + + // Set the background offset used to match the image in the inactive tab + // to the frame image. + void SetBackgroundOffset(const gfx::Point& offset) { + background_offset_ = offset; + } + + // Paints the icon. Most of the time you'll want to invoke Paint directly, but + // in certain situations this invoked outside of Paint. + void PaintIcon(gfx::Canvas* canvas); + + // Returns the minimum possible size of a single unselected Tab. + static gfx::Size GetMinimumUnselectedSize(); + // Returns the minimum possible size of a selected Tab. Selected tabs must + // always show a close button and have a larger minimum size than unselected + // tabs. + static gfx::Size GetMinimumSelectedSize(); + // Returns the preferred size of a single Tab, assuming space is + // available. + static gfx::Size GetStandardSize(); + + // Returns the width for mini-tabs. Mini-tabs always have this width. + static int GetMiniWidth(); + + // Loads the images to be used for the tab background. + static void LoadTabImages(); + + protected: + virtual const gfx::Rect& title_bounds() const { return title_bounds_; } + + // BaseTab overrides: + virtual void DataChanged(const TabRendererData& old); private: - // views::View overrides: + // Overridden from views::View: + virtual void Paint(gfx::Canvas* canvas); + virtual void Layout(); + virtual void ThemeChanged(); + virtual std::string GetClassName() const { return kViewClassName; } virtual bool HasHitTestMask() const; - virtual void GetHitTestMask(gfx::Path* mask) const; - virtual bool OnMousePressed(const views::MouseEvent& event); - virtual bool OnMouseDragged(const views::MouseEvent& event); - virtual void OnMouseReleased(const views::MouseEvent& event, - bool canceled); - virtual bool GetTooltipText(const gfx::Point& p, std::wstring* tooltip); + virtual void GetHitTestMask(gfx::Path* path) const; virtual bool GetTooltipTextOrigin(const gfx::Point& p, gfx::Point* origin); - virtual std::string GetClassName() const { return kTabClassName; } - virtual bool GetAccessibleRole(AccessibilityTypes::Role* role); - // views::ContextMenuController overrides: - virtual void ShowContextMenu(views::View* source, - const gfx::Point& p, - bool is_mouse_gesture); + // Paint various portions of the Tab + void PaintTabBackground(gfx::Canvas* canvas); + void PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas* canvas); + void PaintInactiveTabBackground(gfx::Canvas* canvas); + void PaintActiveTabBackground(gfx::Canvas* canvas); + void PaintAsNewTab(gfx::Canvas* canvas); + + // Returns the number of favicon-size elements that can fit in the tab's + // current size. + int IconCapacity() const; + + // Returns whether the Tab should display a favicon. + bool ShouldShowIcon() const; + + // Returns whether the Tab should display a close button. + bool ShouldShowCloseBox() const; + + // Gets the throb value for the tab. When a tab is not selected the + // active background is drawn at |GetThrobValue()|%. This is used for hover, + // mini tab title change and pulsing. + double GetThrobValue(); + + // The bounds of various sections of the display. + gfx::Rect favicon_bounds_; + gfx::Rect title_bounds_; + + // The offset used to paint the inactive background image. + gfx::Point background_offset_; + + // Hover animation. + scoped_ptr<SlideAnimation> hover_animation_; + + // Animation used when the title of an inactive mini tab changes. + scoped_ptr<MultiAnimation> mini_title_animation_; + + struct TabImage { + SkBitmap* image_l; + SkBitmap* image_c; + SkBitmap* image_r; + int l_width; + int r_width; + int y_offset; + }; + static TabImage tab_active; + static TabImage tab_active_nano; + static TabImage tab_inactive; + static TabImage tab_inactive_nano; + static TabImage tab_alpha; + static TabImage tab_alpha_nano; + + // Whether we're showing the icon. It is cached so that we can detect when it + // changes and layout appropriately. + bool showing_icon_; + + // Whether we are showing the close button. It is cached so that we can + // detect when it changes and layout appropriately. + bool showing_close_button_; + + // The current color of the close button. + SkColor close_button_color_; + + // See description above setter. + bool render_as_new_tab_; + + // See description above setter. + bool render_unselected_; - // views::ButtonListener overrides: - virtual void ButtonPressed(views::Button* sender, const views::Event& event); + // See description above setter. + double alpha_; - // Creates a path that contains the clickable region of the tab's visual - // representation. Used by GetViewForPoint for hit-testing. - void MakePathForTab(gfx::Path* path) const; + static bool initialized_; DISALLOW_COPY_AND_ASSIGN(Tab); }; diff --git a/chrome/browser/views/tabs/tab_controller.h b/chrome/browser/views/tabs/tab_controller.h new file mode 100755 index 0000000..937d3be --- /dev/null +++ b/chrome/browser/views/tabs/tab_controller.h @@ -0,0 +1,50 @@ +// Copyright (c) 2010 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_VIEWS_TABS_TAB_CONTROLLER_H_ +#define CHROME_BROWSER_VIEWS_TABS_TAB_CONTROLLER_H_ + +class BaseTab; + +namespace gfx { +class Point; +} +namespace views { +class MouseEvent; +} + +// Controller for tabs. +class TabController { + public: + // Selects the tab. + virtual void SelectTab(BaseTab* tab) = 0; + + // Closes the tab. + virtual void CloseTab(BaseTab* tab) = 0; + + // Shows a context menu for the tab at the specified point in screen coords. + virtual void ShowContextMenu(BaseTab* tab, const gfx::Point& p) = 0; + + // Returns true if the specified Tab is selected. + virtual bool IsTabSelected(const BaseTab* tab) const = 0; + + // Returns true if the specified Tab is pinned. + virtual bool IsTabPinned(const BaseTab* tab) const = 0; + + // Potentially starts a drag for the specified Tab. + virtual void MaybeStartDrag(BaseTab* tab, const views::MouseEvent& event) = 0; + + // Continues dragging a Tab. + virtual void ContinueDrag(const views::MouseEvent& event) = 0; + + // Ends dragging a Tab. |canceled| is true if the drag was aborted in a way + // other than the user releasing the mouse. Returns whether the tab has been + // destroyed. + virtual bool EndDrag(bool canceled) = 0; + + protected: + virtual ~TabController() {} +}; + +#endif // CHROME_BROWSER_VIEWS_TABS_TAB_CONTROLLER_H_ diff --git a/chrome/browser/views/tabs/tab_renderer.cc b/chrome/browser/views/tabs/tab_renderer.cc deleted file mode 100644 index f32d718..0000000 --- a/chrome/browser/views/tabs/tab_renderer.cc +++ /dev/null @@ -1,1062 +0,0 @@ -// Copyright (c) 2010 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. - -#include "chrome/browser/views/tabs/tab_renderer.h" - -#include <limits> - -#include "app/animation_container.h" -#include "app/l10n_util.h" -#include "app/multi_animation.h" -#include "app/resource_bundle.h" -#include "app/slide_animation.h" -#include "app/throb_animation.h" -#include "base/command_line.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/browser.h" -#include "chrome/browser/browser_theme_provider.h" -#include "chrome/browser/defaults.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/common/chrome_switches.h" -#include "gfx/canvas.h" -#include "gfx/favicon_size.h" -#include "gfx/font.h" -#include "gfx/skbitmap_operations.h" -#include "grit/app_resources.h" -#include "grit/generated_resources.h" -#include "grit/theme_resources.h" -#include "third_party/skia/include/effects/SkGradientShader.h" -#include "views/widget/widget.h" -#include "views/window/non_client_view.h" -#include "views/window/window.h" - -#ifdef WIN32 -#include "app/win_util.h" -#endif - -static const int kLeftPadding = 16; -static const int kTopPadding = 6; -static const int kRightPadding = 15; -static const int kBottomPadding = 5; -static const int kDropShadowHeight = 2; -static const int kToolbarOverlap = 1; -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 kSelectedTitleColor = SK_ColorBLACK; - -// Vertical adjustment to the favicon when the tab has a large icon. -static const int kAppTapFaviconVerticalAdjustment = 2; - -// When a non-mini-tab becomes a mini-tab the width of the tab animates. If -// the width of a mini-tab is >= kMiniTabRendererAsNormalTabWidth then the tab -// is rendered as a normal tab. This is done to avoid having the title -// immediately disappear when transitioning a tab from normal to mini-tab. -static const int kMiniTabRendererAsNormalTabWidth = - browser_defaults::kMiniTabWidth + 30; - -// How long the hover state takes. -static const int kHoverDurationMs = 90; - -// How long the pulse throb takes. -static const int kPulseDurationMs = 200; - -// How opaque to make the hover state (out of 1). -static const double kHoverOpacity = 0.33; - -// TODO(beng): (Cleanup) This stuff should move onto the class. -static gfx::Font* title_font = NULL; -static int title_font_height = 0; -static SkBitmap* close_button_n = NULL; -static SkBitmap* close_button_m = 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* 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; - -// Used when |render_as_new_tab| is true. -static SkBitmap* new_tab_mask = NULL; -static SkBitmap* new_tab_shadow = NULL; - -TabRenderer::TabImage TabRenderer::tab_alpha = {0}; -TabRenderer::TabImage TabRenderer::tab_active = {0}; -TabRenderer::TabImage TabRenderer::tab_active_nano = {0}; -TabRenderer::TabImage TabRenderer::tab_inactive = {0}; -TabRenderer::TabImage TabRenderer::tab_inactive_nano = {0}; -TabRenderer::TabImage TabRenderer::tab_alpha_nano = {0}; - -// Durations for the various parts of the mini tab title animation. -static const int kMiniTitleChangeAnimationDuration1MS = 1000; -static const int kMiniTitleChangeAnimationDuration2MS = 500; -static const int kMiniTitleChangeAnimationDuration3MS = 800; - -// Offset from the right edge for the start of the mini title change animation. -static const int kMiniTitleChangeInitialXOffset = 6; - -// Radius of the radial gradient used for mini title change animation. -static const int kMiniTitleChangeGradientRadius = 20; - -// Colors of the gradient used during the mini title change animation. -static const SkColor kMiniTitleChangeGradientColor1 = SK_ColorWHITE; -static const SkColor kMiniTitleChangeGradientColor2 = - SkColorSetARGB(0, 255, 255, 255); - -namespace { - -void InitResources() { - static bool initialized = false; - if (!initialized) { - // TODO(glen): Allow theming of these. - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - title_font = new gfx::Font(rb.GetFont(ResourceBundle::BaseFont)); - title_font_height = title_font->height(); - - close_button_n = rb.GetBitmapNamed(IDR_TAB_CLOSE); - close_button_m = rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK); - 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(); - - TabRenderer::LoadTabImages(); - - // 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(); - - // We get a DIV0 further down when the throbber is replaced by an image - // which is taller than wide. In this case we cannot deduce an animation - // sequence from it since we assume that each animation frame has the width - // of the image's height. - if (loading_animation_frame_count == 0) { -#ifdef WIN32 - // TODO(idanan): Remove this when we have a way to handle theme errors. - // See: http://code.google.com/p/chromium/issues/detail?id=12531 - // For now, this is Windows-specific because some users have downloaded - // a DLL from outside of Google to override the theme. - std::wstring text = l10n_util::GetString(IDS_RESOURCE_ERROR); - std::wstring caption = l10n_util::GetString(IDS_RESOURCE_ERROR_CAPTION); - UINT flags = MB_OK | MB_ICONWARNING | MB_TOPMOST; - win_util::MessageBox(NULL, text, caption, flags); -#endif - CHECK(loading_animation_frame_count) << "Invalid throbber size. Width = " - << loading_animation_frames->width() << ", height = " - << 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; - // TODO(beng): eventually remove this when we have a proper themeing system. - // themes not supporting IDR_THROBBER_WAITING are causing this - // value to be 0 which causes DIV0 crashes. The value of 5 - // matches the current bitmaps in our source. - if (waiting_to_loading_frame_count_ratio == 0) - waiting_to_loading_frame_count_ratio = 5; - - crashed_fav_icon = rb.GetBitmapNamed(IDR_SAD_FAVICON); - - 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 views::ImageButton { - public: - explicit TabCloseButton(views::ButtonListener* listener) - : views::ImageButton(listener) { - } - virtual ~TabCloseButton() {} - - virtual bool OnMousePressed(const views::MouseEvent& event) { - bool handled = ImageButton::OnMousePressed(event); - // Explicitly mark midle-mouse clicks as non-handled to ensure the tab - // sees them. - return event.IsOnlyMiddleMouseButton() ? false : handled; - } - - // 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 views::MouseEvent& event) { - CustomButton::OnMouseEntered(event); - GetParent()->OnMouseEntered(event); - } - - virtual void OnMouseExited(const views::MouseEvent& event) { - CustomButton::OnMouseExited(event); - GetParent()->OnMouseExited(event); - } - - private: - DISALLOW_COPY_AND_ASSIGN(TabCloseButton); -}; - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// FaviconCrashAnimation -// -// A custom animation subclass to manage the favicon crash animation. -class TabRenderer::FavIconCrashAnimation : public LinearAnimation, - public AnimationDelegate { - public: - explicit FavIconCrashAnimation(TabRenderer* target) - : ALLOW_THIS_IN_INITIALIZER_LIST(LinearAnimation(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_COPY_AND_ASSIGN(FavIconCrashAnimation); -}; - -//////////////////////////////////////////////////////////////////////////////// -// TabRenderer, public: - -TabRenderer::TabRenderer(TabController* controller) - : BaseTabRenderer(controller), - animation_frame_(0), - throbber_disabled_(false), - showing_icon_(false), - showing_close_button_(false), - fav_icon_hiding_offset_(0), - close_button_color_(NULL), - render_as_new_tab_(false), - render_unselected_(false), - alpha_(1), - crash_animation_(NULL), - should_display_crashed_favicon_(false), - theme_provider_(NULL) { - InitResources(); - - // Add the Close Button. - close_button_ = new TabCloseButton(this); - close_button_->SetImage(views::CustomButton::BS_NORMAL, close_button_n); - close_button_->SetImage(views::CustomButton::BS_HOT, close_button_h); - close_button_->SetImage(views::CustomButton::BS_PUSHED, close_button_p); - AddChildView(close_button_); - - hover_animation_.reset(new SlideAnimation(this)); - hover_animation_->SetSlideDuration(kHoverDurationMs); - - pulse_animation_.reset(new ThrobAnimation(this)); - pulse_animation_->SetSlideDuration(kPulseDurationMs); -} - -TabRenderer::~TabRenderer() { - delete crash_animation_; -} - -void TabRenderer::SizeToNewTabButtonImages() { - SetBounds(x(), y(), new_tab_shadow->width(), new_tab_shadow->height()); -} - -void TabRenderer::ViewHierarchyChanged(bool is_add, View* parent, View* child) { - if (parent->GetThemeProvider()) - SetThemeProvider(parent->GetThemeProvider()); -} - -ThemeProvider* TabRenderer::GetThemeProvider() { - ThemeProvider* tp = View::GetThemeProvider(); - if (tp) - return tp; - - if (theme_provider_) - return theme_provider_; - - return NULL; -} - -bool TabRenderer::IsSelected() const { - return true; -} - -void TabRenderer::StartPulse() { - pulse_animation_->Reset(); - pulse_animation_->StartThrobbing(std::numeric_limits<int>::max()); -} - -void TabRenderer::StopPulse() { - pulse_animation_->Stop(); -} - -void TabRenderer::StartMiniTabTitleAnimation() { - if (!mini_title_animation_.get()) { - MultiAnimation::Parts parts; - parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration1MS, - Tween::EASE_OUT)); - parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration2MS, - Tween::ZERO)); - parts.push_back(MultiAnimation::Part(kMiniTitleChangeAnimationDuration3MS, - Tween::EASE_IN)); - mini_title_animation_.reset(new MultiAnimation(parts)); - mini_title_animation_->SetContainer(container_.get()); - mini_title_animation_->set_delegate(this); - } - mini_title_animation_->Start(); -} - -void TabRenderer::StopMiniTabTitleAnimation() { - if (mini_title_animation_.get()) - mini_title_animation_->Stop(); -} - -void TabRenderer::SetAnimationContainer(AnimationContainer* container) { - container_ = container; - pulse_animation_->SetContainer(container); -} - -void TabRenderer::PaintIcon(gfx::Canvas* canvas) { - if (data().network_state != TabRendererData::NETWORK_STATE_NONE) { - PaintLoadingAnimation(canvas); - } else { - canvas->save(); - canvas->ClipRectInt(0, 0, width(), height()); - 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()) { - // TODO(pkasting): Use code in tab_icon_view.cc:PaintIcon() (or switch - // to using that class to render the favicon). - int x = favicon_bounds_.x(); - int y = favicon_bounds_.y() + fav_icon_hiding_offset_; - int size = data().favicon.width(); - canvas->DrawBitmapInt(data().favicon, 0, 0, - data().favicon.width(), - data().favicon.height(), - x, y, size, size, - true); - } - } - canvas->restore(); - } -} - -// static -gfx::Size TabRenderer::GetMinimumUnselectedSize() { - 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.image_l->height()); - return minimum_size; -} - -// static -gfx::Size TabRenderer::GetMinimumSelectedSize() { - gfx::Size minimum_size = GetMinimumUnselectedSize(); - minimum_size.set_width(kLeftPadding + kFavIconSize + kRightPadding); - return minimum_size; -} - -// static -gfx::Size TabRenderer::GetStandardSize() { - gfx::Size standard_size = GetMinimumUnselectedSize(); - standard_size.set_width( - standard_size.width() + kFavIconTitleSpacing + kStandardTitleWidth); - return standard_size; -} - -// static -int TabRenderer::GetMiniWidth() { - return browser_defaults::kMiniTabWidth; -} - -//////////////////////////////////////////////////////////////////////////////// -// TabRenderer, protected: - -std::wstring TabRenderer::GetTitle() const { - return UTF16ToWideHack(data().title); -} - -void TabRenderer::OnMouseEntered(const views::MouseEvent& e) { - hover_animation_->SetTweenType(Tween::EASE_OUT); - hover_animation_->Show(); -} - -void TabRenderer::OnMouseExited(const views::MouseEvent& e) { - hover_animation_->SetTweenType(Tween::EASE_IN); - hover_animation_->Hide(); -} - -void TabRenderer::DataChanged(const TabRendererData& old) { - if (data().phantom) - StopMiniTabTitleAnimation(); - - // Sets the accessible name for the tab. - SetAccessibleName(UTF16ToWide(data().title)); - - if (data().blocked != old.blocked) { - if (data().blocked) - StartPulse(); - else - StopPulse(); - } - - // If this is an extension app and a command line flag is set, - // then disable the throbber. - throbber_disabled_ = data().app && - CommandLine::ForCurrentProcess()->HasSwitch(switches::kAppsNoThrob); - - if (data().crashed) { - if (!should_display_crashed_favicon_ && !IsPerformingCrashAnimation()) - StartCrashAnimation(); - } else { - if (IsPerformingCrashAnimation()) - StopCrashAnimation(); - ResetCrashedFavIcon(); - } -} - -void TabRenderer::AdvanceLoadingAnimation(TabRendererData::NetworkState state) { - if (throbber_disabled_) - return; - - // 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 (state == TabRendererData::NETWORK_STATE_WAITING && - state == TabRendererData::NETWORK_STATE_LOADING) { - animation_frame_ = loading_animation_frame_count - - (animation_frame_ / waiting_to_loading_frame_count_ratio); - } - - if (state != TabRendererData::NETWORK_STATE_NONE) { - animation_frame_ = ++animation_frame_ % - ((state == TabRendererData::NETWORK_STATE_WAITING) ? - waiting_animation_frame_count : - loading_animation_frame_count); - } else { - animation_frame_ = 0; - } - - SchedulePaint(); -} - -//////////////////////////////////////////////////////////////////////////////// -// TabRenderer, views::View overrides: - -void TabRenderer::Paint(gfx::Canvas* canvas) { - if (render_as_new_tab_) { - if (base::i18n::IsRTL()) { - canvas->TranslateInt(width(), 0); - canvas->ScaleInt(-1, 1); - } - PaintAsNewTab(canvas); - return; - } - - // Don't paint if we're narrower than we can render correctly. (This should - // only happen during animations). - if (width() < GetMinimumUnselectedSize().width() && !data().mini) - return; - - // See if the model changes whether the icons should be painted. - const bool show_icon = ShouldShowIcon() && !data().phantom; - const bool show_close_button = ShouldShowCloseBox(); - if (show_icon != showing_icon_ || - show_close_button != showing_close_button_) - Layout(); - - PaintTabBackground(canvas); - - SkColor title_color = GetThemeProvider()-> - GetColor(IsSelected() ? - BrowserThemeProvider::COLOR_TAB_TEXT : - BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT); - - if (!data().mini || width() > kMiniTabRendererAsNormalTabWidth) - PaintTitle(title_color, canvas); - - if (show_icon) - PaintIcon(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; - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - close_button_->SetBackground(close_button_color_, - rb.GetBitmapNamed(IDR_TAB_CLOSE), - rb.GetBitmapNamed(IDR_TAB_CLOSE_MASK)); - } -} - -void TabRenderer::Layout() { - gfx::Rect lb = GetLocalBounds(false); - if (lb.IsEmpty()) - return; - lb.Inset(kLeftPadding, kTopPadding, kRightPadding, kBottomPadding); - - // First of all, figure out who is tallest. - int content_height = GetContentHeight(); - - // Size the Favicon. - showing_icon_ = ShouldShowIcon(); - if (showing_icon_) { - // Use the size of the favicon as apps use a bigger favicon size. - int favicon_size = - !data().favicon.empty() ? data().favicon.width() : kFavIconSize; - int favicon_top = kTopPadding + content_height / 2 - favicon_size / 2; - int favicon_left = lb.x(); - if (favicon_size != kFavIconSize) { - favicon_left -= (favicon_size - kFavIconSize) / 2; - favicon_top -= kAppTapFaviconVerticalAdjustment; - } - favicon_bounds_.SetRect(favicon_left, favicon_top, - favicon_size, favicon_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() - favicon_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); - } - } - } else { - favicon_bounds_.SetRect(lb.x(), lb.y(), 0, 0); - } - - // 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); - } - - int title_left = favicon_bounds_.right() + kFavIconTitleSpacing; - int title_top = kTopPadding + (content_height - title_font_height) / 2; - // Size the Title text to fill the remaining space. - if (!data().mini || width() >= kMiniTabRendererAsNormalTabWidth) { - // 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 = GetMinimumUnselectedSize(); - 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_->x() - - kTitleCloseButtonSpacing - title_left, 0); - } else { - title_width = std::max(lb.width() - title_left, 0); - } - title_bounds_.SetRect(title_left, title_top, title_width, - title_font_height); - } else { - title_bounds_.SetRect(title_left, title_top, 0, 0); - } - - // Certain UI elements within the Tab (the favicon, 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 views::Button - // instance) are automatically mirrored by the mirroring infrastructure in - // views. 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_)); -} - -void TabRenderer::ThemeChanged() { - LoadTabImages(); - View::ThemeChanged(); -} - -/////////////////////////////////////////////////////////////////////////////// -// TabRenderer, AnimationDelegate implementation: - -void TabRenderer::AnimationProgressed(const Animation* animation) { - SchedulePaint(); -} - -void TabRenderer::AnimationCanceled(const Animation* animation) { - AnimationEnded(animation); -} - -void TabRenderer::AnimationEnded(const Animation* animation) { - SchedulePaint(); -} - -//////////////////////////////////////////////////////////////////////////////// -// TabRenderer, private - -void TabRenderer::PaintTitle(SkColor title_color, gfx::Canvas* canvas) { - // Paint the Title. - string16 title = data().title; - if (title.empty()) { - title = data().loading ? - l10n_util::GetStringUTF16(IDS_TAB_LOADING_TITLE) : - TabContents::GetDefaultTitle(); - } else { - Browser::FormatTitleForDisplay(&title); - } - - canvas->DrawStringInt(UTF16ToWideHack(title), *title_font, title_color, - title_bounds_.x(), title_bounds_.y(), - title_bounds_.width(), title_bounds_.height()); -} - -void TabRenderer::PaintTabBackground(gfx::Canvas* canvas) { - if (IsSelected()) { - PaintActiveTabBackground(canvas); - } else { - if (mini_title_animation_.get() && mini_title_animation_->is_animating()) - PaintInactiveTabBackgroundWithTitleChange(canvas); - else - PaintInactiveTabBackground(canvas); - - double throb_value = GetThrobValue(); - if (throb_value > 0) { - SkRect bounds; - bounds.set(0, 0, SkIntToScalar(width()), SkIntToScalar(height())); - canvas->saveLayerAlpha(&bounds, static_cast<int>(throb_value * 0xff), - SkCanvas::kARGB_ClipLayer_SaveFlag); - canvas->drawARGB(0, 255, 255, 255, SkXfermode::kClear_Mode); - PaintActiveTabBackground(canvas); - canvas->restore(); - } - } -} - -void TabRenderer::PaintInactiveTabBackgroundWithTitleChange( - gfx::Canvas* canvas) { - // Render the inactive tab background. We'll use this for clipping. - gfx::Canvas background_canvas(width(), height(), false); - PaintInactiveTabBackground(&background_canvas); - - SkBitmap background_image = background_canvas.ExtractBitmap(); - - // Draw a radial gradient to hover_canvas. - gfx::Canvas hover_canvas(width(), height(), false); - int radius = kMiniTitleChangeGradientRadius; - int x0 = width() + radius - kMiniTitleChangeInitialXOffset; - int x1 = radius; - int x2 = -radius; - int x; - if (mini_title_animation_->current_part_index() == 0) { - x = mini_title_animation_->CurrentValueBetween(x0, x1); - } else if (mini_title_animation_->current_part_index() == 1) { - x = x1; - } else { - x = mini_title_animation_->CurrentValueBetween(x1, x2); - } - SkPaint paint; - SkPoint loc = { SkIntToScalar(x), SkIntToScalar(0) }; - SkColor colors[2]; - colors[0] = kMiniTitleChangeGradientColor1; - colors[1] = kMiniTitleChangeGradientColor2; - SkShader* shader = SkGradientShader::CreateRadial( - loc, - SkIntToScalar(radius), - colors, - NULL, - 2, - SkShader::kClamp_TileMode); - paint.setShader(shader); - shader->unref(); - hover_canvas.FillRectInt(x - radius, -radius, radius * 2, radius * 2, paint); - - // Draw the radial gradient clipped to the background into hover_image. - SkBitmap hover_image = SkBitmapOperations::CreateMaskedBitmap( - hover_canvas.ExtractBitmap(), background_image); - - // Draw the tab background to the canvas. - canvas->DrawBitmapInt(background_image, 0, 0); - - // And then the gradient on top of that. - if (mini_title_animation_->current_part_index() == 2) { - canvas->saveLayerAlpha(NULL, - mini_title_animation_->CurrentValueBetween(255, 0)); - canvas->DrawBitmapInt(hover_image, 0, 0); - canvas->restore(); - } else { - canvas->DrawBitmapInt(hover_image, 0, 0); - } -} - -void TabRenderer::PaintInactiveTabBackground(gfx::Canvas* canvas) { - bool is_otr = data().off_the_record; - - // The tab image needs to be lined up with the background image - // so that it feels partially transparent. These offsets represent the tab - // position within the frame background image. - int offset = GetX(views::View::APPLY_MIRRORING_TRANSFORMATION) + - background_offset_.x(); - - int tab_id; - if (GetWidget() && - GetWidget()->GetWindow()->GetNonClientView()->UseNativeFrame()) { - tab_id = IDR_THEME_TAB_BACKGROUND_V; - } else { - tab_id = is_otr ? IDR_THEME_TAB_BACKGROUND_INCOGNITO : - IDR_THEME_TAB_BACKGROUND; - } - - SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(tab_id); - - // App tabs are drawn slightly differently (as nano tabs). - TabImage* tab_image = data().app ? &tab_active_nano : &tab_active; - TabImage* tab_inactive_image = data().app ? &tab_inactive_nano : - &tab_inactive; - TabImage* alpha = data().app ? &tab_alpha_nano : &tab_alpha; - - // If the theme is providing a custom background image, then its top edge - // should be at the top of the tab. Otherwise, we assume that the background - // image is a composited foreground + frame image. - int bg_offset_y = GetThemeProvider()->HasCustomImage(tab_id) ? - 0 : background_offset_.y(); - - // Draw left edge. Don't draw over the toolbar, as we're not the foreground - // tab. - SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap( - *tab_bg, offset, bg_offset_y, tab_image->l_width, height()); - SkBitmap theme_l = - SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l); - canvas->DrawBitmapInt(theme_l, - 0, 0, theme_l.width(), theme_l.height() - kToolbarOverlap, - 0, 0, theme_l.width(), theme_l.height() - kToolbarOverlap, - false); - - // Draw right edge. Again, don't draw over the toolbar. - SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg, - offset + width() - tab_image->r_width, bg_offset_y, - tab_image->r_width, height()); - SkBitmap theme_r = - SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r); - canvas->DrawBitmapInt(theme_r, - 0, 0, theme_r.width(), theme_r.height() - kToolbarOverlap, - width() - theme_r.width(), 0, theme_r.width(), - theme_r.height() - kToolbarOverlap, false); - - // Draw center. Instead of masking out the top portion we simply skip over - // it by incrementing by kDropShadowHeight, since it's a simple rectangle. - // And again, don't draw over the toolbar. - canvas->TileImageInt(*tab_bg, - offset + tab_image->l_width, - bg_offset_y + kDropShadowHeight + tab_image->y_offset, - tab_image->l_width, - kDropShadowHeight + tab_image->y_offset, - width() - tab_image->l_width - tab_image->r_width, - height() - kDropShadowHeight - kToolbarOverlap - tab_image->y_offset); - - // Now draw the highlights/shadows around the tab edge. - canvas->DrawBitmapInt(*tab_inactive_image->image_l, 0, 0); - canvas->TileImageInt(*tab_inactive_image->image_c, - tab_inactive_image->l_width, 0, - width() - tab_inactive_image->l_width - - tab_inactive_image->r_width, - height()); - canvas->DrawBitmapInt(*tab_inactive_image->image_r, - width() - tab_inactive_image->r_width, 0); -} - -void TabRenderer::PaintActiveTabBackground(gfx::Canvas* canvas) { - int offset = GetX(views::View::APPLY_MIRRORING_TRANSFORMATION) + - background_offset_.x(); - ThemeProvider* tp = GetThemeProvider(); - if (!tp) - NOTREACHED() << "Unable to get theme provider"; - - SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(IDR_THEME_TOOLBAR); - - // App tabs are drawn slightly differently (as nano tabs). - TabImage* tab_image = data().app ? &tab_active_nano : &tab_active; - TabImage* alpha = data().app ? &tab_alpha_nano : &tab_alpha; - - // Draw left edge. - SkBitmap tab_l = SkBitmapOperations::CreateTiledBitmap( - *tab_bg, offset, 0, tab_image->l_width, height()); - SkBitmap theme_l = - SkBitmapOperations::CreateMaskedBitmap(tab_l, *alpha->image_l); - canvas->DrawBitmapInt(theme_l, 0, 0); - - // Draw right edge. - SkBitmap tab_r = SkBitmapOperations::CreateTiledBitmap(*tab_bg, - offset + width() - tab_image->r_width, 0, tab_image->r_width, height()); - SkBitmap theme_r = - SkBitmapOperations::CreateMaskedBitmap(tab_r, *alpha->image_r); - canvas->DrawBitmapInt(theme_r, width() - tab_image->r_width, 0); - - // Draw center. Instead of masking out the top portion we simply skip over it - // by incrementing by kDropShadowHeight, since it's a simple rectangle. - canvas->TileImageInt(*tab_bg, - offset + tab_image->l_width, - kDropShadowHeight + tab_image->y_offset, - tab_image->l_width, - kDropShadowHeight + tab_image->y_offset, - width() - tab_image->l_width - tab_image->r_width, - height() - kDropShadowHeight - tab_image->y_offset); - - // Now draw the highlights/shadows around the tab edge. - canvas->DrawBitmapInt(*tab_image->image_l, 0, 0); - canvas->TileImageInt(*tab_image->image_c, tab_image->l_width, 0, - width() - tab_image->l_width - tab_image->r_width, height()); - canvas->DrawBitmapInt(*tab_image->image_r, width() - tab_image->r_width, 0); -} - -void TabRenderer::PaintLoadingAnimation(gfx::Canvas* canvas) { - SkBitmap* frames = - (data().network_state == TabRendererData::NETWORK_STATE_WAITING) ? - waiting_animation_frames : loading_animation_frames; - int image_size = frames->height(); - int image_offset = animation_frame_ * image_size; - int dst_y = (height() - 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 (data().mini) { - dst_x = favicon_bounds_.x(); - if (favicon_bounds_.width() != kFavIconSize) - dst_x += (favicon_bounds_.width() - kFavIconSize) / 2; - } else { - if (base::i18n::IsRTL()) { - dst_x = width() - 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); -} - -void TabRenderer::PaintAsNewTab(gfx::Canvas* canvas) { - bool is_otr = data().off_the_record; - - // The tab image needs to be lined up with the background image - // so that it feels partially transparent. These offsets represent the tab - // position within the frame background image. - int offset = GetX(views::View::APPLY_MIRRORING_TRANSFORMATION) + - background_offset_.x(); - - int tab_id; - if (GetWidget() && - GetWidget()->GetWindow()->GetNonClientView()->UseNativeFrame()) { - tab_id = IDR_THEME_TAB_BACKGROUND_V; - } else { - tab_id = is_otr ? IDR_THEME_TAB_BACKGROUND_INCOGNITO : - IDR_THEME_TAB_BACKGROUND; - } - - SkBitmap* tab_bg = GetThemeProvider()->GetBitmapNamed(tab_id); - - // If the theme is providing a custom background image, then its top edge - // should be at the top of the tab. Otherwise, we assume that the background - // image is a composited foreground + frame image. - int bg_offset_y = GetThemeProvider()->HasCustomImage(tab_id) ? - 0 : background_offset_.y(); - - SkBitmap image = SkBitmapOperations::CreateTiledBitmap( - *tab_bg, offset, bg_offset_y, new_tab_mask->width(), - new_tab_mask->height()); - image = SkBitmapOperations::CreateMaskedBitmap(image, *new_tab_mask); - canvas->DrawBitmapInt(image, - 0, 0, image.width(), image.height(), - 0, 0, image.width(), image.height(), - false); - - canvas->DrawBitmapInt(*new_tab_shadow, - 0, 0, new_tab_shadow->width(), new_tab_shadow->height(), - 0, 0, new_tab_shadow->width(), new_tab_shadow->height(), - false); -} - -int TabRenderer::IconCapacity() const { - if (height() < GetMinimumUnselectedSize().height()) - return 0; - return (width() - kLeftPadding - kRightPadding) / kFavIconSize; -} - -bool TabRenderer::ShouldShowIcon() const { - if (data().mini && height() >= GetMinimumUnselectedSize().height()) - return true; - 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 !data().mini && (IsSelected() || IconCapacity() >= 3); -} - -double TabRenderer::GetThrobValue() { - if (alpha_ != 1) - return alpha_; - - if (pulse_animation_->is_animating()) - return pulse_animation_->GetCurrentValue() * kHoverOpacity; - - return hover_animation_.get() ? - kHoverOpacity * hover_animation_->GetCurrentValue() : 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// TabRenderer, private: - -void TabRenderer::StartCrashAnimation() { - if (!crash_animation_) - crash_animation_ = new FavIconCrashAnimation(this); - crash_animation_->Stop(); - crash_animation_->Start(); -} - -void TabRenderer::StopCrashAnimation() { - if (!crash_animation_) - return; - crash_animation_->Stop(); -} - -bool TabRenderer::IsPerformingCrashAnimation() const { - return crash_animation_ && crash_animation_->is_animating(); -} - -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; -} - -// static -void TabRenderer::LoadTabImages() { - // We're not letting people override tab images just yet. - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - - tab_alpha.image_l = rb.GetBitmapNamed(IDR_TAB_ALPHA_LEFT); - tab_alpha.image_r = rb.GetBitmapNamed(IDR_TAB_ALPHA_RIGHT); - - tab_alpha_nano.image_l = rb.GetBitmapNamed(IDR_TAB_ALPHA_NANO_LEFT); - tab_alpha_nano.image_r = rb.GetBitmapNamed(IDR_TAB_ALPHA_NANO_RIGHT); - - tab_active.image_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_LEFT); - tab_active.image_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_CENTER); - tab_active.image_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_RIGHT); - tab_active.l_width = tab_active.image_l->width(); - tab_active.r_width = tab_active.image_r->width(); - - // The regular tab is much taller *visually* than the nano tabs. - // The images are the same height, this is really just the difference - // in whitespace above the tab image (regular vs nano). - const int kNanoTabDiffHeight = 13; - - tab_active_nano.image_l = rb.GetBitmapNamed(IDR_TAB_ACTIVE_NANO_LEFT); - tab_active_nano.image_c = rb.GetBitmapNamed(IDR_TAB_ACTIVE_NANO_CENTER); - tab_active_nano.image_r = rb.GetBitmapNamed(IDR_TAB_ACTIVE_NANO_RIGHT); - tab_active_nano.l_width = tab_active_nano.image_l->width(); - tab_active_nano.r_width = tab_active_nano.image_r->width(); - tab_active_nano.y_offset = kNanoTabDiffHeight; - - tab_inactive.image_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_LEFT); - tab_inactive.image_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_CENTER); - tab_inactive.image_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_RIGHT); - tab_inactive.l_width = tab_inactive.image_l->width(); - tab_inactive.r_width = tab_inactive.image_r->width(); - - tab_inactive_nano.image_l = rb.GetBitmapNamed(IDR_TAB_INACTIVE_NANO_LEFT); - tab_inactive_nano.image_c = rb.GetBitmapNamed(IDR_TAB_INACTIVE_NANO_CENTER); - tab_inactive_nano.image_r = rb.GetBitmapNamed(IDR_TAB_INACTIVE_NANO_RIGHT); - tab_inactive_nano.l_width = tab_inactive_nano.image_l->width(); - tab_inactive_nano.r_width = tab_inactive_nano.image_r->width(); - tab_inactive_nano.y_offset = kNanoTabDiffHeight; - - loading_animation_frames = rb.GetBitmapNamed(IDR_THROBBER); - waiting_animation_frames = rb.GetBitmapNamed(IDR_THROBBER_WAITING); - - new_tab_mask = rb.GetBitmapNamed(IDR_TAB_ALPHA_NEW_TAB); - new_tab_shadow = rb.GetBitmapNamed(IDR_TAB_NEW_TAB_SHADOW); -} diff --git a/chrome/browser/views/tabs/tab_renderer.h b/chrome/browser/views/tabs/tab_renderer.h deleted file mode 100644 index 7247b50..0000000 --- a/chrome/browser/views/tabs/tab_renderer.h +++ /dev/null @@ -1,248 +0,0 @@ -// Copyright (c) 2010 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_VIEWS_TABS_TAB_RENDERER_H_ -#define CHROME_BROWSER_VIEWS_TABS_TAB_RENDERER_H_ - -#include "app/animation.h" -#include "base/scoped_ptr.h" -#include "chrome/browser/views/tabs/base_tab_renderer.h" -#include "gfx/point.h" -#include "views/controls/button/image_button.h" - -class AnimationContainer; -class MultiAnimation; -class SlideAnimation; -class ThrobAnimation; - -/////////////////////////////////////////////////////////////////////////////// -// -// TabRenderer -// -// A View that renders a Tab, either in a TabStrip or in a DraggedTabView. -// -/////////////////////////////////////////////////////////////////////////////// -class TabRenderer : public BaseTabRenderer, - public views::ButtonListener, - public AnimationDelegate { - public: - explicit TabRenderer(TabController* controller); - virtual ~TabRenderer(); - - // Sizes the renderer to the size of the new tab images. This is used - // during the new tab animation. See TabStrip's description of AnimationType - // for details. - void SizeToNewTabButtonImages(); - - // Overridden from views: - void ViewHierarchyChanged(bool is_add, View* parent, View* child); - ThemeProvider* GetThemeProvider(); - - // Used during new tab animation to force the tab to render a new tab like - // animation. - void set_render_as_new_tab(bool value) { render_as_new_tab_ = value; } - - // Sets the alpha value to render the tab at. This is used during the new - // tab animation. - void set_alpha(double value) { alpha_ = value; } - - // Forces the tab to render unselected even though it is selected. - void set_render_unselected(bool value) { render_unselected_ = value; } - bool render_unselected() const { return render_unselected_; } - - // Returns true if the Tab is selected, false otherwise. - virtual bool IsSelected() const; - - // Starts/Stops a pulse animation. - void StartPulse(); - void StopPulse(); - - // Start/stop the mini-tab title animation. - void StartMiniTabTitleAnimation(); - void StopMiniTabTitleAnimation(); - - // Set the background offset used to match the image in the inactive tab - // to the frame image. - void SetBackgroundOffset(const gfx::Point& offset) { - background_offset_ = offset; - } - - // Set the theme provider - because we get detached, we are frequently - // outside of a hierarchy with a theme provider at the top. This should be - // called whenever we're detached or attached to a hierarchy. - void SetThemeProvider(ThemeProvider* provider) { - theme_provider_ = provider; - } - - // Sets the container all animations run from. - void SetAnimationContainer(AnimationContainer* container); - - // Paints the icon. Most of the time you'll want to invoke Paint directly, but - // in certain situations this invoked outside of Paint. - void PaintIcon(gfx::Canvas* canvas); - - // Returns the minimum possible size of a single unselected Tab. - static gfx::Size GetMinimumUnselectedSize(); - // Returns the minimum possible size of a selected Tab. Selected tabs must - // always show a close button and have a larger minimum size than unselected - // tabs. - static gfx::Size GetMinimumSelectedSize(); - // Returns the preferred size of a single Tab, assuming space is - // available. - static gfx::Size GetStandardSize(); - - // Returns the width for mini-tabs. Mini-tabs always have this width. - static int GetMiniWidth(); - - // Loads the images to be used for the tab background. - static void LoadTabImages(); - - protected: - views::ImageButton* close_button() const { return close_button_; } - const gfx::Rect& title_bounds() const { return title_bounds_; } - - // Returns the title of the Tab. - std::wstring GetTitle() const; - - // Overridden from views::View: - virtual void OnMouseEntered(const views::MouseEvent& event); - virtual void OnMouseExited(const views::MouseEvent& event); - - // views::ButtonListener overrides: - virtual void ButtonPressed(views::Button* sender, - const views::Event& event) {} - - // BaseTabRenderer overrides: - virtual void DataChanged(const TabRendererData& old); - virtual void AdvanceLoadingAnimation(TabRendererData::NetworkState state); - - private: - // Overridden from views::View: - virtual void Paint(gfx::Canvas* canvas); - virtual void Layout(); - virtual void ThemeChanged(); - - // Overridden from AnimationDelegate: - virtual void AnimationProgressed(const Animation* animation); - virtual void AnimationCanceled(const Animation* animation); - virtual void AnimationEnded(const Animation* animation); - - // Starts/Stops the crash animation. - void StartCrashAnimation(); - void StopCrashAnimation(); - - // Return true if the crash animation is currently running. - bool IsPerformingCrashAnimation() const; - - // Set the temporary offset for the favicon. This is used during animation. - void SetFavIconHidingOffset(int offset); - - void DisplayCrashedFavIcon(); - void ResetCrashedFavIcon(); - - // Paint various portions of the Tab - void PaintTitle(SkColor title_color, gfx::Canvas* canvas); - void PaintTabBackground(gfx::Canvas* canvas); - void PaintInactiveTabBackgroundWithTitleChange(gfx::Canvas* canvas); - void PaintInactiveTabBackground(gfx::Canvas* canvas); - void PaintActiveTabBackground(gfx::Canvas* canvas); - void PaintLoadingAnimation(gfx::Canvas* canvas); - void PaintAsNewTab(gfx::Canvas* canvas); - - // Returns the number of favicon-size elements that can fit in the tab's - // current size. - int IconCapacity() const; - - // Returns whether the Tab should display a favicon. - bool ShouldShowIcon() const; - - // Returns whether the Tab should display a close button. - bool ShouldShowCloseBox() const; - - // Gets the throb value for the tab. When a tab is not selected the - // active background is drawn at |GetThrobValue()|%. This is used for hover, - // mini tab title change and pulsing. - double GetThrobValue(); - - // The bounds of various sections of the display. - gfx::Rect favicon_bounds_; - gfx::Rect title_bounds_; - - // The offset used to paint the inactive background image. - gfx::Point background_offset_; - - // The current index into the Animation image strip. - int animation_frame_; - - // Close Button. - views::ImageButton* close_button_; - - // Whether to disable throbber animations. Only true if this is a - // Nano tab renderer and a command line flag has been passed in to - // disable the animations. - bool throbber_disabled_; - - // Hover animation. - scoped_ptr<SlideAnimation> hover_animation_; - - // Pulse animation. - scoped_ptr<ThrobAnimation> pulse_animation_; - - // Animation used when the title of an inactive mini tab changes. - scoped_ptr<MultiAnimation> mini_title_animation_; - - struct TabImage { - SkBitmap* image_l; - SkBitmap* image_c; - SkBitmap* image_r; - int l_width; - int r_width; - int y_offset; - }; - static TabImage tab_active; - static TabImage tab_active_nano; - static TabImage tab_inactive; - static TabImage tab_inactive_nano; - static TabImage tab_alpha; - static TabImage tab_alpha_nano; - - // Whether we're showing the icon. It is cached so that we can detect when it - // changes and layout appropriately. - bool showing_icon_; - - // Whether we are showing the close button. It is cached so that we can - // detect when it changes and layout appropriately. - bool showing_close_button_; - - // The offset used to animate the favicon location. - int fav_icon_hiding_offset_; - - // The current color of the close button. - SkColor close_button_color_; - - // See description above setter. - bool render_as_new_tab_; - - // See description above setter. - bool render_unselected_; - - // See description above setter. - double alpha_; - - // The animation object used to swap the favicon with the sad tab icon. - class FavIconCrashAnimation; - FavIconCrashAnimation* crash_animation_; - - bool should_display_crashed_favicon_; - - ThemeProvider* theme_provider_; - - scoped_refptr<AnimationContainer> container_; - - static bool initialized_; - - DISALLOW_COPY_AND_ASSIGN(TabRenderer); -}; - -#endif // CHROME_BROWSER_VIEWS_TABS_TAB_RENDERER_H_ diff --git a/chrome/browser/views/tabs/tab_strip.cc b/chrome/browser/views/tabs/tab_strip.cc index c6e45fa..2c1af2a 100644 --- a/chrome/browser/views/tabs/tab_strip.cc +++ b/chrome/browser/views/tabs/tab_strip.cc @@ -110,7 +110,7 @@ class NewTabAlphaDelegate class ResetDraggingStateDelegate : public views::BoundsAnimator::OwnedAnimationDelegate { public: - explicit ResetDraggingStateDelegate(BaseTabRenderer* tab) : tab_(tab) { + explicit ResetDraggingStateDelegate(BaseTab* tab) : tab_(tab) { } virtual void AnimationEnded(const Animation* animation) { @@ -122,7 +122,7 @@ class ResetDraggingStateDelegate } private: - BaseTabRenderer* tab_; + BaseTab* tab_; DISALLOW_COPY_AND_ASSIGN(ResetDraggingStateDelegate); }; @@ -419,11 +419,11 @@ void TabStrip::StopAllHighlighting() { GetTabAtTabDataIndex(i)->StopPulse(); } -BaseTabRenderer* TabStrip::CreateTabForDragging() { +BaseTab* TabStrip::CreateTabForDragging() { Tab* tab = new Tab(NULL); // Make sure the dragged tab shares our theme provider. We need to explicitly // do this as during dragging there isn't a theme provider. - tab->SetThemeProvider(GetThemeProvider()); + tab->set_theme_provider(GetThemeProvider()); return tab; } @@ -610,7 +610,7 @@ views::View* TabStrip::GetViewForPoint(const gfx::Point& point) { // Return any view that isn't a Tab or this TabStrip immediately. We don't // want to interfere. views::View* v = View::GetViewForPoint(point); - if (v && v != this && v->GetClassName() != Tab::kTabClassName) + if (v && v != this && v->GetClassName() != Tab::kViewClassName) return v; // The display order doesn't necessarily match the child list order, so we @@ -643,9 +643,9 @@ void TabStrip::OnBoundsAnimatorDone(views::BoundsAnimator* animator) { NewTabAnimation2Done(); } -Tab* TabStrip::CreateTab() { +BaseTab* TabStrip::CreateTab() { Tab* tab = new Tab(this); - tab->SetAnimationContainer(animation_container_.get()); + tab->set_animation_container(animation_container_.get()); return tab; } @@ -665,7 +665,7 @@ void TabStrip::StartMoveTabAnimation() { AnimateToIdealBounds(); } -void TabStrip::StartedDraggingTab(BaseTabRenderer* tab) { +void TabStrip::StartedDraggingTab(BaseTab* tab) { tab->set_dragging(true); // Stop any animations on the tab. @@ -679,7 +679,7 @@ void TabStrip::StartedDraggingTab(BaseTabRenderer* tab) { SchedulePaint(); } -void TabStrip::StoppedDraggingTab(BaseTabRenderer* tab) { +void TabStrip::StoppedDraggingTab(BaseTab* tab) { int tab_data_index = TabIndexOfTab(tab); if (tab_data_index == -1) { // The tab was removed before the drag completed. Don't do anything. @@ -709,7 +709,7 @@ void TabStrip::ViewHierarchyChanged(bool is_add, /////////////////////////////////////////////////////////////////////////////// // TabStrip, Tab::Delegate implementation: -bool TabStrip::IsTabSelected(const BaseTabRenderer* btr) const { +bool TabStrip::IsTabSelected(const BaseTab* btr) const { const Tab* tab = static_cast<const Tab*>(btr); if (tab->closing() || tab->render_unselected()) return false; @@ -1159,7 +1159,7 @@ void TabStrip::GenerateIdealBounds() { int mini_tab_count = 0; int nano_tab_count = 0; for (int i = 0; i < tab_count(); ++i) { - BaseTabRenderer* tab = base_tab_at_tab_index(i); + BaseTab* tab = base_tab_at_tab_index(i); if (!tab->closing()) { ++non_closing_tab_count; if (tab->data().mini) @@ -1327,12 +1327,12 @@ void TabStrip::StartInsertTabAnimationImpl(int model_index) { GenerateIdealBounds(); int tab_data_index = ModelIndexToTabIndex(model_index); - BaseTabRenderer* tab = base_tab_at_tab_index(tab_data_index); + BaseTab* tab = base_tab_at_tab_index(tab_data_index); if (model_index == 0) { tab->SetBounds(0, ideal_bounds(tab_data_index).y(), 0, ideal_bounds(tab_data_index).height()); } else { - BaseTabRenderer* last_tab = base_tab_at_tab_index(tab_data_index - 1); + BaseTab* last_tab = base_tab_at_tab_index(tab_data_index - 1); tab->SetBounds(last_tab->bounds().right() + kTabHOffset, ideal_bounds(tab_data_index).y(), 0, ideal_bounds(tab_data_index).height()); diff --git a/chrome/browser/views/tabs/tab_strip.h b/chrome/browser/views/tabs/tab_strip.h index 0eadbc1..7486f46 100644 --- a/chrome/browser/views/tabs/tab_strip.h +++ b/chrome/browser/views/tabs/tab_strip.h @@ -10,12 +10,13 @@ #include "base/ref_counted.h" #include "base/timer.h" #include "chrome/browser/views/tabs/base_tab_strip.h" -#include "chrome/browser/views/tabs/tab.h" #include "gfx/point.h" #include "gfx/rect.h" #include "views/animation/bounds_animator.h" #include "views/controls/button/image_button.h" +class Tab; + namespace views { class ImageView; #if defined(OS_LINUX) @@ -76,7 +77,7 @@ class TabStrip : public BaseTabStrip, virtual void SetTabData(int model_index, const TabRendererData& data); virtual void StartHighlight(int model_index); virtual void StopAllHighlighting(); - virtual BaseTabRenderer* CreateTabForDragging(); + virtual BaseTab* CreateTabForDragging(); // views::View overrides: virtual void PaintChildren(gfx::Canvas* canvas); @@ -98,11 +99,11 @@ class TabStrip : public BaseTabStrip, protected: // BaseTabStrip overrides: - virtual Tab* CreateTab(); + virtual BaseTab* CreateTab(); virtual void StartInsertTabAnimation(int model_index, bool foreground); virtual void StartMoveTabAnimation(); - virtual void StartedDraggingTab(BaseTabRenderer* tab); - virtual void StoppedDraggingTab(BaseTabRenderer* tab); + virtual void StartedDraggingTab(BaseTab* tab); + virtual void StoppedDraggingTab(BaseTab* tab); // views::View implementation: virtual void ViewHierarchyChanged(bool is_add, @@ -110,7 +111,7 @@ class TabStrip : public BaseTabStrip, views::View* child); // TabController overrides. - virtual bool IsTabSelected(const BaseTabRenderer* btr) const; + virtual bool IsTabSelected(const BaseTab* btr) const; // views::ButtonListener implementation: virtual void ButtonPressed(views::Button* sender, const views::Event& event); diff --git a/chrome/browser/views/tabs/tab_strip_controller.h b/chrome/browser/views/tabs/tab_strip_controller.h index 90cd87c..9f6d0cc 100644 --- a/chrome/browser/views/tabs/tab_strip_controller.h +++ b/chrome/browser/views/tabs/tab_strip_controller.h @@ -7,7 +7,7 @@ #include <vector> -class BaseTabRenderer; +class BaseTab; class BaseTabStrip; class GURL; @@ -46,7 +46,7 @@ class TabStripController { virtual void CloseTab(int index) = 0; // Shows a context menu for the tab at the specified point in screen coords. - virtual void ShowContextMenu(BaseTabRenderer* tab, const gfx::Point& p) = 0; + virtual void ShowContextMenu(BaseTab* tab, const gfx::Point& p) = 0; // Updates the loading animations of all the tabs. virtual void UpdateLoadingAnimations() = 0; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 745952d..55a75ce 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -2467,8 +2467,8 @@ 'browser/views/tab_contents/tab_contents_view_gtk.h', 'browser/views/tab_contents/tab_contents_view_win.cc', 'browser/views/tab_contents/tab_contents_view_win.h', - 'browser/views/tabs/base_tab_renderer.cc', - 'browser/views/tabs/base_tab_renderer.h', + 'browser/views/tabs/base_tab.cc', + 'browser/views/tabs/base_tab.h', 'browser/views/tabs/base_tab_strip.cc', 'browser/views/tabs/base_tab_strip.h', 'browser/views/tabs/browser_tab_strip_controller.cc', @@ -2488,8 +2488,7 @@ 'browser/views/tabs/side_tab_strip.h', 'browser/views/tabs/tab.cc', 'browser/views/tabs/tab.h', - 'browser/views/tabs/tab_renderer.cc', - 'browser/views/tabs/tab_renderer.h', + 'browser/views/tabs/tab_controller.h', 'browser/views/tabs/tab_renderer_data.h', 'browser/views/tabs/tab_strip.cc', 'browser/views/tabs/tab_strip.h', @@ -3045,8 +3044,8 @@ ['include', '^browser/views/tab_contents/tab_contents_view_gtk.h'], ['include', '^browser/views/tab_icon_view.cc'], ['include', '^browser/views/tab_icon_view.h'], - ['include', '^browser/views/tabs/base_tab_renderer.cc'], - ['include', '^browser/views/tabs/base_tab_renderer.h'], + ['include', '^browser/views/tabs/base_tab.cc'], + ['include', '^browser/views/tabs/base_tab.h'], ['include', '^browser/views/tabs/base_tab_strip.cc'], ['include', '^browser/views/tabs/base_tab_strip.h'], ['include', '^browser/views/tabs/browser_tab_strip_controller.cc'], @@ -3066,8 +3065,7 @@ ['include', '^browser/views/tabs/side_tab_strip.h'], ['include', '^browser/views/tabs/tab.cc'], ['include', '^browser/views/tabs/tab.h'], - ['include', '^browser/views/tabs/tab_renderer.cc'], - ['include', '^browser/views/tabs/tab_renderer.h'], + ['include', '^browser/views/tabs/tab_controller.h'], ['include', '^browser/views/tabs/tab_renderer_data.h'], ['include', '^browser/views/tabs/tab_strip.cc'], ['include', '^browser/views/tabs/tab_strip.h'], |