| Index: chrome/browser/ui/views/tabs/side_tab_strip.cc
|
| diff --git a/chrome/browser/ui/views/tabs/side_tab_strip.cc b/chrome/browser/ui/views/tabs/side_tab_strip.cc
|
| index 4e1ae21f4e81a6464847c94faf1bdb2b9795194a..1c6fa8703041826eace9e1a9e4b7faf33ab7e670 100644
|
| --- a/chrome/browser/ui/views/tabs/side_tab_strip.cc
|
| +++ b/chrome/browser/ui/views/tabs/side_tab_strip.cc
|
| @@ -14,16 +14,24 @@
|
| #include "ui/gfx/canvas.h"
|
| #include "views/background.h"
|
| #include "views/controls/button/image_button.h"
|
| +#include "views/controls/button/text_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 scroll buttons.
|
| +const int kScrollButtonHeight = 20;
|
| +
|
| // Height of the separator.
|
| const int kSeparatorHeight = 1;
|
|
|
| +// Padding between tabs and scroll button.
|
| +const int kScrollButtonVerticalPadding = 2;
|
| +
|
| // The new tab button is rendered using a SideTab.
|
| class SideTabNewTabButton : public SideTab {
|
| public:
|
| @@ -61,6 +69,49 @@ void SideTabNewTabButton::OnMouseReleased(const views::MouseEvent& event) {
|
| controller_->CreateNewTab();
|
| }
|
|
|
| +// Button class used for the scroll buttons.
|
| +class ScrollButton : public views::TextButton {
|
| + public:
|
| + enum Type {
|
| + UP,
|
| + DOWN
|
| + };
|
| +
|
| + explicit ScrollButton(views::ButtonListener* listener, Type type);
|
| +
|
| + protected:
|
| + // views::View overrides.
|
| + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
|
| +
|
| + private:
|
| + const Type type_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ScrollButton);
|
| +};
|
| +
|
| +ScrollButton::ScrollButton(views::ButtonListener* listener,
|
| + Type type)
|
| + : views::TextButton(listener, std::wstring()),
|
| + type_(type) {
|
| +}
|
| +
|
| +void ScrollButton::OnPaint(gfx::Canvas* canvas) {
|
| + TextButton::OnPaint(canvas);
|
| +
|
| + // Draw the arrow.
|
| + SkColor arrow_color = IsEnabled() ? SK_ColorBLACK : SK_ColorGRAY;
|
| + int arrow_height = 5;
|
| + int x = width() / 2;
|
| + int y = (height() - arrow_height) / 2;
|
| + int delta_y = 1;
|
| + if (type_ == DOWN) {
|
| + delta_y = -1;
|
| + y += arrow_height;
|
| + }
|
| + for (int i = 0; i < arrow_height; ++i, --x, y += delta_y)
|
| + canvas->FillRectInt(arrow_color, x, y, (i * 2) + 1, 1);
|
| +}
|
| +
|
| } // namespace
|
|
|
| // static
|
| @@ -72,13 +123,21 @@ const int SideTabStrip::kTabStripInset = 3;
|
| SideTabStrip::SideTabStrip(TabStripController* controller)
|
| : BaseTabStrip(controller, BaseTabStrip::VERTICAL_TAB_STRIP),
|
| newtab_button_(new SideTabNewTabButton(controller)),
|
| - separator_(new views::View()) {
|
| + scroll_up_button_(NULL),
|
| + scroll_down_button_(NULL),
|
| + separator_(new views::View()),
|
| + first_tab_y_offset_(0),
|
| + ideal_height_(0) {
|
| SetID(VIEW_ID_TAB_STRIP);
|
| set_background(views::Background::CreateSolidBackground(kBackgroundColor));
|
| AddChildView(newtab_button_);
|
| separator_->set_background(
|
| views::Background::CreateSolidBackground(kSeparatorColor));
|
| AddChildView(separator_);
|
| + scroll_up_button_ = new ScrollButton(this, ScrollButton::UP);
|
| + AddChildView(scroll_up_button_);
|
| + scroll_down_button_ = new ScrollButton(this, ScrollButton::DOWN);
|
| + AddChildView(scroll_down_button_);
|
| }
|
|
|
| SideTabStrip::~SideTabStrip() {
|
| @@ -118,6 +177,9 @@ void SideTabStrip::RemoveTabAt(int model_index) {
|
|
|
| void SideTabStrip::SelectTabAt(int old_model_index, int new_model_index) {
|
| GetBaseTabAtModelIndex(new_model_index)->SchedulePaint();
|
| +
|
| + if (controller()->IsActiveTab(new_model_index))
|
| + MakeTabVisible(ModelIndexToTabIndex(new_model_index));
|
| }
|
|
|
| void SideTabStrip::TabTitleChangedNotLoading(int model_index) {
|
| @@ -132,6 +194,12 @@ void SideTabStrip::PaintChildren(gfx::Canvas* canvas) {
|
| // them last.
|
| std::vector<BaseTab*> dragging_tabs;
|
|
|
| + // Make sure nothing draws on top of the scroll buttons.
|
| + canvas->Save();
|
| + canvas->ClipRectInt(kTabStripInset, kTabStripInset,
|
| + width() - kTabStripInset - kTabStripInset,
|
| + GetMaxTabY() - kTabStripInset);
|
| +
|
| // Paint the new tab and separator first so that any tabs animating appear on
|
| // top.
|
| separator_->Paint(canvas);
|
| @@ -147,6 +215,74 @@ void SideTabStrip::PaintChildren(gfx::Canvas* canvas) {
|
|
|
| for (size_t i = 0; i < dragging_tabs.size(); ++i)
|
| dragging_tabs[i]->Paint(canvas);
|
| +
|
| + canvas->Restore();
|
| +
|
| + scroll_down_button_->Paint(canvas);
|
| + scroll_up_button_->Paint(canvas);
|
| +}
|
| +
|
| +views::View* SideTabStrip::GetEventHandlerForPoint(const gfx::Point& point) {
|
| + // Check the scroll buttons first as they visually appear on top of everything
|
| + // else.
|
| + if (scroll_down_button_->IsVisible()) {
|
| + gfx::Point local_point(point);
|
| + View::ConvertPointToView(this, scroll_down_button_, &local_point);
|
| + if (scroll_down_button_->HitTest(local_point))
|
| + return scroll_down_button_->GetEventHandlerForPoint(local_point);
|
| + }
|
| +
|
| + if (scroll_up_button_->IsVisible()) {
|
| + gfx::Point local_point(point);
|
| + View::ConvertPointToView(this, scroll_up_button_, &local_point);
|
| + if (scroll_up_button_->HitTest(local_point))
|
| + return scroll_up_button_->GetEventHandlerForPoint(local_point);
|
| + }
|
| + return views::View::GetEventHandlerForPoint(point);
|
| +}
|
| +
|
| +void SideTabStrip::ButtonPressed(views::Button* sender,
|
| + const views::Event& event) {
|
| + int max_offset = GetMaxOffset();
|
| + if (max_offset == 0) {
|
| + // All the tabs fit, no need to scroll.
|
| + return;
|
| + }
|
| +
|
| + // Determine the index of the first visible tab.
|
| + int initial_y = kTabStripInset;
|
| + int first_vis_index = -1;
|
| + for (int i = 0; i < tab_count(); ++i) {
|
| + if (ideal_bounds(i).bottom() > initial_y) {
|
| + first_vis_index = i;
|
| + break;
|
| + }
|
| + }
|
| + if (first_vis_index == -1)
|
| + return;
|
| +
|
| + int delta = 0;
|
| + if (sender == scroll_up_button_) {
|
| + delta = initial_y - ideal_bounds(first_vis_index).y();
|
| + if (delta <= 0) {
|
| + if (first_vis_index == 0) {
|
| + delta = -first_tab_y_offset_;
|
| + } else {
|
| + delta = initial_y - ideal_bounds(first_vis_index - 1).y();
|
| + DCHECK_NE(0, delta); // Not fatal, but indicates we aren't scrolling.
|
| + }
|
| + }
|
| + } else {
|
| + DCHECK_EQ(sender, scroll_down_button_);
|
| + if (ideal_bounds(first_vis_index).y() > initial_y) {
|
| + delta = initial_y - ideal_bounds(first_vis_index).y();
|
| + } else if (first_vis_index + 1 == tab_count()) {
|
| + delta = -first_tab_y_offset_;
|
| + } else {
|
| + delta = initial_y - ideal_bounds(first_vis_index + 1).y();
|
| + }
|
| + }
|
| + SetFirstTabYOffset(first_tab_y_offset_ + delta);
|
| }
|
|
|
| BaseTab* SideTabStrip::CreateTab() {
|
| @@ -162,7 +298,7 @@ void SideTabStrip::GenerateIdealBounds() {
|
| gfx::Rect layout_rect = GetContentsBounds();
|
| layout_rect.Inset(kTabStripInset, kTabStripInset);
|
|
|
| - int y = layout_rect.y();
|
| + int y = layout_rect.y() + first_tab_y_offset_;
|
| bool last_was_mini = true;
|
| bool has_non_closing_tab = false;
|
| separator_bounds_.SetRect(0, -kSeparatorHeight, width(), kSeparatorHeight);
|
| @@ -196,7 +332,14 @@ void SideTabStrip::GenerateIdealBounds() {
|
| newtab_button_bounds_ =
|
| gfx::Rect(layout_rect.x(), y, layout_rect.width(),
|
| newtab_button_->GetPreferredSize().height());
|
| + y += newtab_button_->GetPreferredSize().height();
|
| }
|
| +
|
| + ideal_height_ = y - layout_rect.y() - first_tab_y_offset_;
|
| +
|
| + scroll_up_button_->SetEnabled(first_tab_y_offset_ != 0);
|
| + scroll_down_button_->SetEnabled(GetMaxOffset() != 0 &&
|
| + first_tab_y_offset_ != GetMaxOffset());
|
| }
|
|
|
| void SideTabStrip::StartInsertTabAnimation(int model_index) {
|
| @@ -232,10 +375,13 @@ void SideTabStrip::AnimateToIdealBounds() {
|
|
|
| void SideTabStrip::DoLayout() {
|
| BaseTabStrip::DoLayout();
|
| -
|
| newtab_button_->SetBoundsRect(newtab_button_bounds_);
|
| -
|
| separator_->SetBoundsRect(separator_bounds_);
|
| + int scroll_button_y = height() - kScrollButtonHeight;
|
| + scroll_up_button_->SetBounds(0, scroll_button_y, width() / 2,
|
| + kScrollButtonHeight);
|
| + scroll_down_button_->SetBounds(width() / 2, scroll_button_y, width() / 2,
|
| + kScrollButtonHeight);
|
| }
|
|
|
| void SideTabStrip::LayoutDraggedTabsAt(const std::vector<BaseTab*>& tabs,
|
| @@ -271,3 +417,55 @@ void SideTabStrip::CalculateBoundsForDraggedTabs(
|
| int SideTabStrip::GetSizeNeededForTabs(const std::vector<BaseTab*>& tabs) {
|
| return static_cast<int>(tabs.size()) * SideTab::GetPreferredHeight();
|
| }
|
| +
|
| +void SideTabStrip::OnBoundsChanged(const gfx::Rect& previous_bounds) {
|
| + // When our height changes we may be able to show more.
|
| + first_tab_y_offset_ = std::max(GetMaxOffset(),
|
| + std::min(0, first_tab_y_offset_));
|
| + for (int i = 0; i < controller()->GetCount(); ++i) {
|
| + if (controller()->IsActiveTab(i)) {
|
| + MakeTabVisible(ModelIndexToTabIndex(i));
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void SideTabStrip::SetFirstTabYOffset(int new_offset) {
|
| + int max_offset = GetMaxOffset();
|
| + if (max_offset == 0) {
|
| + // All the tabs fit, no need to scroll.
|
| + return;
|
| + }
|
| + new_offset = std::max(max_offset, std::min(0, new_offset));
|
| + if (new_offset == first_tab_y_offset_)
|
| + return;
|
| +
|
| + StopAnimating(false);
|
| + first_tab_y_offset_ = new_offset;
|
| + GenerateIdealBounds();
|
| + DoLayout();
|
| +
|
| +}
|
| +
|
| +int SideTabStrip::GetMaxOffset() const {
|
| + int available_height = GetMaxTabY() - kTabStripInset;
|
| + return std::min(0, available_height - ideal_height_);
|
| +}
|
| +
|
| +int SideTabStrip::GetMaxTabY() const {
|
| + return height() - kTabStripInset - kScrollButtonVerticalPadding -
|
| + kScrollButtonHeight;
|
| +}
|
| +
|
| +void SideTabStrip::MakeTabVisible(int tab_index) {
|
| + if (height() == 0)
|
| + return;
|
| +
|
| + if (ideal_bounds(tab_index).y() < kTabStripInset) {
|
| + SetFirstTabYOffset(first_tab_y_offset_ - ideal_bounds(tab_index).y() +
|
| + kTabStripInset);
|
| + } else if (ideal_bounds(tab_index).bottom() > GetMaxTabY()) {
|
| + SetFirstTabYOffset(GetMaxTabY() - (ideal_bounds(tab_index).bottom() -
|
| + first_tab_y_offset_));
|
| + }
|
| +}
|
|
|