// 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/side_tab_strip.h" #include "app/l10n_util.h" #include "app/resource_bundle.h" #include "chrome/browser/views/tabs/side_tab.h" #include "chrome/browser/views/tabs/tab_strip_controller.h" #include "chrome/browser/view_ids.h" #include "gfx/canvas.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "views/background.h" #include "views/controls/button/image_button.h" namespace { const int kVerticalTabSpacing = 2; const int kTabStripWidth = 140; const SkColor kBackgroundColor = SkColorSetARGB(255, 209, 220, 248); const SkColor kSeparatorColor = SkColorSetARGB(255, 151, 159, 179); // Height of the separator. const int kSeparatorHeight = 1; // The new tab button is rendered using a SideTab. class SideTabNewTabButton : public SideTab { public: explicit SideTabNewTabButton(TabStripController* controller); virtual bool ShouldPaintHighlight() const { return false; } virtual bool IsSelected() const { return false; } bool OnMousePressed(const views::MouseEvent& event); void OnMouseReleased(const views::MouseEvent& event, bool canceled); private: TabStripController* controller_; DISALLOW_COPY_AND_ASSIGN(SideTabNewTabButton); }; SideTabNewTabButton::SideTabNewTabButton(TabStripController* controller) : SideTab(NULL), controller_(controller) { // Never show a close button for the new tab button. close_button()->SetVisible(false); TabRendererData data; data.favicon = *ResourceBundle::GetSharedInstance().GetBitmapNamed( IDR_SIDETABS_NEW_TAB); data.title = l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE); SetData(data); } bool SideTabNewTabButton::OnMousePressed(const views::MouseEvent& event) { return true; } void SideTabNewTabButton::OnMouseReleased(const views::MouseEvent& event, bool canceled) { if (!canceled && event.IsOnlyLeftMouseButton() && HitTest(event.location())) controller_->CreateNewTab(); } } // namespace // static const int SideTabStrip::kTabStripInset = 3; //////////////////////////////////////////////////////////////////////////////// // SideTabStrip, public: SideTabStrip::SideTabStrip(TabStripController* controller) : BaseTabStrip(controller, BaseTabStrip::VERTICAL_TAB_STRIP), newtab_button_(new SideTabNewTabButton(controller)), separator_(new views::View()) { SetID(VIEW_ID_TAB_STRIP); set_background(views::Background::CreateSolidBackground(kBackgroundColor)); AddChildView(newtab_button_); separator_->set_background( views::Background::CreateSolidBackground(kSeparatorColor)); AddChildView(separator_); } SideTabStrip::~SideTabStrip() { DestroyDragController(); } //////////////////////////////////////////////////////////////////////////////// // SideTabStrip, BaseTabStrip implementation: int SideTabStrip::GetPreferredHeight() { return 0; } void SideTabStrip::SetBackgroundOffset(const gfx::Point& offset) { } bool SideTabStrip::IsPositionInWindowCaption(const gfx::Point& point) { return GetViewForPoint(point) == this; } void SideTabStrip::SetDraggedTabBounds(int tab_index, const gfx::Rect& tab_bounds) { } TabStrip* SideTabStrip::AsTabStrip() { return NULL; } void SideTabStrip::StartHighlight(int model_index) { } void SideTabStrip::StopAllHighlighting() { } 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; } void SideTabStrip::RemoveTabAt(int model_index) { StartRemoveTabAnimation(model_index); } void SideTabStrip::SelectTabAt(int old_model_index, int new_model_index) { GetBaseTabAtModelIndex(new_model_index)->SchedulePaint(); } void SideTabStrip::TabTitleChangedNotLoading(int model_index) { } gfx::Size SideTabStrip::GetPreferredSize() { return gfx::Size(kTabStripWidth, 0); } void SideTabStrip::PaintChildren(gfx::Canvas* canvas) { // Make sure the dragged tab appears on top of all others by paint it last. BaseTab* dragging_tab = NULL; // Paint the new tab and separator first so that any tabs animating appear on // top. separator_->ProcessPaint(canvas); newtab_button_->ProcessPaint(canvas); for (int i = tab_count() - 1; i >= 0; --i) { BaseTab* tab = base_tab_at_tab_index(i); if (tab->dragging()) dragging_tab = tab; else tab->ProcessPaint(canvas); } if (dragging_tab) dragging_tab->ProcessPaint(canvas); } BaseTab* SideTabStrip::CreateTab() { return new SideTab(this); } void SideTabStrip::GenerateIdealBounds() { gfx::Rect layout_rect = GetLocalBounds(false); layout_rect.Inset(kTabStripInset, kTabStripInset); int y = layout_rect.y(); bool last_was_mini = true; bool has_non_closing_tab = false; separator_bounds_.SetRect(0, -kSeparatorHeight, width(), kSeparatorHeight); for (int i = 0; i < tab_count(); ++i) { BaseTab* tab = base_tab_at_tab_index(i); if (!tab->closing()) { if (last_was_mini != tab->data().mini) { if (has_non_closing_tab) { separator_bounds_.SetRect(0, y, width(), kSeparatorHeight); y += kSeparatorHeight + kVerticalTabSpacing; } newtab_button_bounds_.SetRect( layout_rect.x(), y, layout_rect.width(), newtab_button_->GetPreferredSize().height()); y = newtab_button_bounds_.bottom() + kVerticalTabSpacing; last_was_mini = tab->data().mini; } gfx::Rect bounds = gfx::Rect(layout_rect.x(), y, layout_rect.width(), tab->GetPreferredSize().height()); set_ideal_bounds(i, bounds); y = bounds.bottom() + kVerticalTabSpacing; has_non_closing_tab = true; } } if (last_was_mini) { if (has_non_closing_tab) { separator_bounds_.SetRect(0, y, width(), kSeparatorHeight); y += kSeparatorHeight + kVerticalTabSpacing; } newtab_button_bounds_ = gfx::Rect(layout_rect.x(), y, layout_rect.width(), newtab_button_->GetPreferredSize().height()); } } void SideTabStrip::StartInsertTabAnimation(int model_index, bool foreground) { PrepareForAnimation(); GenerateIdealBounds(); int tab_data_index = ModelIndexToTabIndex(model_index); BaseTab* tab = base_tab_at_tab_index(tab_data_index); if (model_index == 0) { tab->SetBounds(ideal_bounds(tab_data_index).x(), 0, ideal_bounds(tab_data_index).width(), 0); } else { BaseTab* last_tab = base_tab_at_tab_index(tab_data_index - 1); tab->SetBounds(last_tab->x(), last_tab->bounds().bottom(), ideal_bounds(tab_data_index).width(), 0); } AnimateToIdealBounds(); } void SideTabStrip::StartMoveTabAnimation() { PrepareForAnimation(); GenerateIdealBounds(); AnimateToIdealBounds(); } void SideTabStrip::StopAnimating(bool layout) { if (!IsAnimating()) return; bounds_animator().Cancel(); if (layout) Layout(); } void SideTabStrip::AnimateToIdealBounds() { for (int i = 0; i < tab_count(); ++i) { BaseTab* tab = base_tab_at_tab_index(i); if (!tab->closing() && !tab->dragging()) bounds_animator().AnimateViewTo(tab, ideal_bounds(i)); } bounds_animator().AnimateViewTo(newtab_button_, newtab_button_bounds_); bounds_animator().AnimateViewTo(separator_, separator_bounds_); } void SideTabStrip::Layout() { BaseTabStrip::Layout(); newtab_button_->SetBounds(newtab_button_bounds_); separator_->SetBounds(separator_bounds_); }