Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1092)

Side by Side Diff: chrome/browser/ui/views/tabs/side_tab_strip.cc

Issue 6822006: Adds scroll buttons to side tabs so that you can get to all the tabs. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleanup Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/ui/views/tabs/side_tab_strip.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/tabs/side_tab_strip.h" 5 #include "chrome/browser/ui/views/tabs/side_tab_strip.h"
6 6
7 #include "chrome/browser/ui/view_ids.h" 7 #include "chrome/browser/ui/view_ids.h"
8 #include "chrome/browser/ui/views/tabs/side_tab.h" 8 #include "chrome/browser/ui/views/tabs/side_tab.h"
9 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h" 9 #include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
10 #include "grit/generated_resources.h" 10 #include "grit/generated_resources.h"
11 #include "grit/theme_resources.h" 11 #include "grit/theme_resources.h"
12 #include "ui/base/l10n/l10n_util.h" 12 #include "ui/base/l10n/l10n_util.h"
13 #include "ui/base/resource/resource_bundle.h" 13 #include "ui/base/resource/resource_bundle.h"
14 #include "ui/gfx/canvas.h" 14 #include "ui/gfx/canvas.h"
15 #include "views/background.h" 15 #include "views/background.h"
16 #include "views/controls/button/image_button.h" 16 #include "views/controls/button/image_button.h"
17 #include "views/controls/button/text_button.h"
17 18
18 namespace { 19 namespace {
20
19 const int kVerticalTabSpacing = 2; 21 const int kVerticalTabSpacing = 2;
20 const int kTabStripWidth = 140; 22 const int kTabStripWidth = 140;
21 const SkColor kBackgroundColor = SkColorSetARGB(255, 209, 220, 248); 23 const SkColor kBackgroundColor = SkColorSetARGB(255, 209, 220, 248);
22 const SkColor kSeparatorColor = SkColorSetARGB(255, 151, 159, 179); 24 const SkColor kSeparatorColor = SkColorSetARGB(255, 151, 159, 179);
23 25
26 // Height of the scroll buttons.
27 const int kScrollButtonHeight = 20;
28
24 // Height of the separator. 29 // Height of the separator.
25 const int kSeparatorHeight = 1; 30 const int kSeparatorHeight = 1;
26 31
32 // Padding between tabs and scroll button.
33 const int kScrollButtonVerticalPadding = 2;
34
27 // The new tab button is rendered using a SideTab. 35 // The new tab button is rendered using a SideTab.
28 class SideTabNewTabButton : public SideTab { 36 class SideTabNewTabButton : public SideTab {
29 public: 37 public:
30 explicit SideTabNewTabButton(TabStripController* controller); 38 explicit SideTabNewTabButton(TabStripController* controller);
31 39
32 virtual bool ShouldPaintHighlight() const OVERRIDE { return false; } 40 virtual bool ShouldPaintHighlight() const OVERRIDE { return false; }
33 virtual bool IsSelected() const OVERRIDE { return false; } 41 virtual bool IsSelected() const OVERRIDE { return false; }
34 virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE; 42 virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
35 virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE; 43 virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
36 44
(...skipping 17 matching lines...) Expand all
54 62
55 bool SideTabNewTabButton::OnMousePressed(const views::MouseEvent& event) { 63 bool SideTabNewTabButton::OnMousePressed(const views::MouseEvent& event) {
56 return true; 64 return true;
57 } 65 }
58 66
59 void SideTabNewTabButton::OnMouseReleased(const views::MouseEvent& event) { 67 void SideTabNewTabButton::OnMouseReleased(const views::MouseEvent& event) {
60 if (event.IsOnlyLeftMouseButton() && HitTest(event.location())) 68 if (event.IsOnlyLeftMouseButton() && HitTest(event.location()))
61 controller_->CreateNewTab(); 69 controller_->CreateNewTab();
62 } 70 }
63 71
72 // Button class used for the scroll buttons.
73 class ScrollButton : public views::TextButton {
74 public:
75 enum Type {
76 UP,
77 DOWN
78 };
79
80 explicit ScrollButton(views::ButtonListener* listener, Type type);
81
82 protected:
83 // views::View overrides.
84 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
85
86 private:
87 const Type type_;
88
89 DISALLOW_COPY_AND_ASSIGN(ScrollButton);
90 };
91
92 ScrollButton::ScrollButton(views::ButtonListener* listener,
93 Type type)
94 : views::TextButton(listener, std::wstring()),
95 type_(type) {
96 }
97
98 void ScrollButton::OnPaint(gfx::Canvas* canvas) {
99 TextButton::OnPaint(canvas);
100
101 // Draw the arrow.
102 SkColor arrow_color = IsEnabled() ? SK_ColorBLACK : SK_ColorGRAY;
103 int arrow_height = 5;
104 int x = width() / 2;
105 int y = (height() - arrow_height) / 2;
106 int delta_y = 1;
107 if (type_ == DOWN) {
108 delta_y = -1;
109 y += arrow_height;
110 }
111 for (int i = 0; i < arrow_height; ++i, --x, y += delta_y)
112 canvas->FillRectInt(arrow_color, x, y, (i * 2) + 1, 1);
113 }
114
64 } // namespace 115 } // namespace
65 116
66 // static 117 // static
67 const int SideTabStrip::kTabStripInset = 3; 118 const int SideTabStrip::kTabStripInset = 3;
68 119
69 //////////////////////////////////////////////////////////////////////////////// 120 ////////////////////////////////////////////////////////////////////////////////
70 // SideTabStrip, public: 121 // SideTabStrip, public:
71 122
72 SideTabStrip::SideTabStrip(TabStripController* controller) 123 SideTabStrip::SideTabStrip(TabStripController* controller)
73 : BaseTabStrip(controller, BaseTabStrip::VERTICAL_TAB_STRIP), 124 : BaseTabStrip(controller, BaseTabStrip::VERTICAL_TAB_STRIP),
74 newtab_button_(new SideTabNewTabButton(controller)), 125 newtab_button_(new SideTabNewTabButton(controller)),
75 separator_(new views::View()) { 126 scroll_up_button_(NULL),
127 scroll_down_button_(NULL),
128 separator_(new views::View()),
129 first_tab_y_offset_(0),
130 ideal_height_(0) {
76 SetID(VIEW_ID_TAB_STRIP); 131 SetID(VIEW_ID_TAB_STRIP);
77 set_background(views::Background::CreateSolidBackground(kBackgroundColor)); 132 set_background(views::Background::CreateSolidBackground(kBackgroundColor));
78 AddChildView(newtab_button_); 133 AddChildView(newtab_button_);
79 separator_->set_background( 134 separator_->set_background(
80 views::Background::CreateSolidBackground(kSeparatorColor)); 135 views::Background::CreateSolidBackground(kSeparatorColor));
81 AddChildView(separator_); 136 AddChildView(separator_);
137 scroll_up_button_ = new ScrollButton(this, ScrollButton::UP);
138 AddChildView(scroll_up_button_);
139 scroll_down_button_ = new ScrollButton(this, ScrollButton::DOWN);
140 AddChildView(scroll_down_button_);
82 } 141 }
83 142
84 SideTabStrip::~SideTabStrip() { 143 SideTabStrip::~SideTabStrip() {
85 DestroyDragController(); 144 DestroyDragController();
86 } 145 }
87 146
88 //////////////////////////////////////////////////////////////////////////////// 147 ////////////////////////////////////////////////////////////////////////////////
89 // SideTabStrip, AbstractTabStripView implementation: 148 // SideTabStrip, AbstractTabStripView implementation:
90 149
91 bool SideTabStrip::IsPositionInWindowCaption(const gfx::Point& point) { 150 bool SideTabStrip::IsPositionInWindowCaption(const gfx::Point& point) {
(...skipping 19 matching lines...) Expand all
111 tab->set_theme_provider(GetThemeProvider()); 170 tab->set_theme_provider(GetThemeProvider());
112 return tab; 171 return tab;
113 } 172 }
114 173
115 void SideTabStrip::RemoveTabAt(int model_index) { 174 void SideTabStrip::RemoveTabAt(int model_index) {
116 StartRemoveTabAnimation(model_index); 175 StartRemoveTabAnimation(model_index);
117 } 176 }
118 177
119 void SideTabStrip::SelectTabAt(int old_model_index, int new_model_index) { 178 void SideTabStrip::SelectTabAt(int old_model_index, int new_model_index) {
120 GetBaseTabAtModelIndex(new_model_index)->SchedulePaint(); 179 GetBaseTabAtModelIndex(new_model_index)->SchedulePaint();
180
181 if (controller()->IsActiveTab(new_model_index))
182 MakeTabVisible(ModelIndexToTabIndex(new_model_index));
121 } 183 }
122 184
123 void SideTabStrip::TabTitleChangedNotLoading(int model_index) { 185 void SideTabStrip::TabTitleChangedNotLoading(int model_index) {
124 } 186 }
125 187
126 gfx::Size SideTabStrip::GetPreferredSize() { 188 gfx::Size SideTabStrip::GetPreferredSize() {
127 return gfx::Size(kTabStripWidth, 0); 189 return gfx::Size(kTabStripWidth, 0);
128 } 190 }
129 191
130 void SideTabStrip::PaintChildren(gfx::Canvas* canvas) { 192 void SideTabStrip::PaintChildren(gfx::Canvas* canvas) {
131 // Make sure any tabs being dragged appear on top of all others by painting 193 // Make sure any tabs being dragged appear on top of all others by painting
132 // them last. 194 // them last.
133 std::vector<BaseTab*> dragging_tabs; 195 std::vector<BaseTab*> dragging_tabs;
134 196
197 // Make sure nothing draws on top of the scroll buttons.
198 canvas->Save();
199 canvas->ClipRectInt(kTabStripInset, kTabStripInset,
200 width() - kTabStripInset - kTabStripInset,
201 GetMaxTabY() - kTabStripInset);
202
135 // Paint the new tab and separator first so that any tabs animating appear on 203 // Paint the new tab and separator first so that any tabs animating appear on
136 // top. 204 // top.
137 separator_->Paint(canvas); 205 separator_->Paint(canvas);
138 newtab_button_->Paint(canvas); 206 newtab_button_->Paint(canvas);
139 207
140 for (int i = tab_count() - 1; i >= 0; --i) { 208 for (int i = tab_count() - 1; i >= 0; --i) {
141 BaseTab* tab = base_tab_at_tab_index(i); 209 BaseTab* tab = base_tab_at_tab_index(i);
142 if (tab->dragging()) 210 if (tab->dragging())
143 dragging_tabs.push_back(tab); 211 dragging_tabs.push_back(tab);
144 else 212 else
145 tab->Paint(canvas); 213 tab->Paint(canvas);
146 } 214 }
147 215
148 for (size_t i = 0; i < dragging_tabs.size(); ++i) 216 for (size_t i = 0; i < dragging_tabs.size(); ++i)
149 dragging_tabs[i]->Paint(canvas); 217 dragging_tabs[i]->Paint(canvas);
218
219 canvas->Restore();
220
221 scroll_down_button_->Paint(canvas);
222 scroll_up_button_->Paint(canvas);
223 }
224
225 views::View* SideTabStrip::GetEventHandlerForPoint(const gfx::Point& point) {
226 // Check the scroll buttons first as they visually appear on top of everything
227 // else.
228 if (scroll_down_button_->IsVisible()) {
229 gfx::Point local_point(point);
230 View::ConvertPointToView(this, scroll_down_button_, &local_point);
231 if (scroll_down_button_->HitTest(local_point))
232 return scroll_down_button_->GetEventHandlerForPoint(local_point);
233 }
234
235 if (scroll_up_button_->IsVisible()) {
236 gfx::Point local_point(point);
237 View::ConvertPointToView(this, scroll_up_button_, &local_point);
238 if (scroll_up_button_->HitTest(local_point))
239 return scroll_up_button_->GetEventHandlerForPoint(local_point);
240 }
241 return views::View::GetEventHandlerForPoint(point);
242 }
243
244 void SideTabStrip::ButtonPressed(views::Button* sender,
245 const views::Event& event) {
246 int max_offset = GetMaxOffset();
247 if (max_offset == 0) {
248 // All the tabs fit, no need to scroll.
249 return;
250 }
251
252 // Determine the index of the first visible tab.
253 int initial_y = kTabStripInset;
254 int first_vis_index = -1;
255 for (int i = 0; i < tab_count(); ++i) {
256 if (ideal_bounds(i).bottom() > initial_y) {
257 first_vis_index = i;
258 break;
259 }
260 }
261 if (first_vis_index == -1)
262 return;
263
264 int delta = 0;
265 if (sender == scroll_up_button_) {
266 delta = initial_y - ideal_bounds(first_vis_index).y();
267 if (delta <= 0) {
268 if (first_vis_index == 0) {
269 delta = -first_tab_y_offset_;
270 } else {
271 delta = initial_y - ideal_bounds(first_vis_index - 1).y();
272 DCHECK_NE(0, delta); // Not fatal, but indicates we aren't scrolling.
273 }
274 }
275 } else {
276 DCHECK_EQ(sender, scroll_down_button_);
277 if (ideal_bounds(first_vis_index).y() > initial_y) {
278 delta = initial_y - ideal_bounds(first_vis_index).y();
279 } else if (first_vis_index + 1 == tab_count()) {
280 delta = -first_tab_y_offset_;
281 } else {
282 delta = initial_y - ideal_bounds(first_vis_index + 1).y();
283 }
284 }
285 SetFirstTabYOffset(first_tab_y_offset_ + delta);
150 } 286 }
151 287
152 BaseTab* SideTabStrip::CreateTab() { 288 BaseTab* SideTabStrip::CreateTab() {
153 return new SideTab(this); 289 return new SideTab(this);
154 } 290 }
155 291
156 bool SideTabStrip::IgnoreTitlePrefixEliding(BaseTab* tab) { 292 bool SideTabStrip::IgnoreTitlePrefixEliding(BaseTab* tab) {
157 DCHECK(tab != NULL); 293 DCHECK(tab != NULL);
158 return tab->data().title.empty(); 294 return tab->data().title.empty();
159 } 295 }
160 296
161 void SideTabStrip::GenerateIdealBounds() { 297 void SideTabStrip::GenerateIdealBounds() {
162 gfx::Rect layout_rect = GetContentsBounds(); 298 gfx::Rect layout_rect = GetContentsBounds();
163 layout_rect.Inset(kTabStripInset, kTabStripInset); 299 layout_rect.Inset(kTabStripInset, kTabStripInset);
164 300
165 int y = layout_rect.y(); 301 int y = layout_rect.y() + first_tab_y_offset_;
166 bool last_was_mini = true; 302 bool last_was_mini = true;
167 bool has_non_closing_tab = false; 303 bool has_non_closing_tab = false;
168 separator_bounds_.SetRect(0, -kSeparatorHeight, width(), kSeparatorHeight); 304 separator_bounds_.SetRect(0, -kSeparatorHeight, width(), kSeparatorHeight);
169 for (int i = 0; i < tab_count(); ++i) { 305 for (int i = 0; i < tab_count(); ++i) {
170 BaseTab* tab = base_tab_at_tab_index(i); 306 BaseTab* tab = base_tab_at_tab_index(i);
171 if (!tab->closing()) { 307 if (!tab->closing()) {
172 if (last_was_mini != tab->data().mini) { 308 if (last_was_mini != tab->data().mini) {
173 if (has_non_closing_tab) { 309 if (has_non_closing_tab) {
174 separator_bounds_.SetRect(0, y, width(), kSeparatorHeight); 310 separator_bounds_.SetRect(0, y, width(), kSeparatorHeight);
175 y += kSeparatorHeight + kVerticalTabSpacing; 311 y += kSeparatorHeight + kVerticalTabSpacing;
(...skipping 13 matching lines...) Expand all
189 } 325 }
190 326
191 if (last_was_mini) { 327 if (last_was_mini) {
192 if (has_non_closing_tab) { 328 if (has_non_closing_tab) {
193 separator_bounds_.SetRect(0, y, width(), kSeparatorHeight); 329 separator_bounds_.SetRect(0, y, width(), kSeparatorHeight);
194 y += kSeparatorHeight + kVerticalTabSpacing; 330 y += kSeparatorHeight + kVerticalTabSpacing;
195 } 331 }
196 newtab_button_bounds_ = 332 newtab_button_bounds_ =
197 gfx::Rect(layout_rect.x(), y, layout_rect.width(), 333 gfx::Rect(layout_rect.x(), y, layout_rect.width(),
198 newtab_button_->GetPreferredSize().height()); 334 newtab_button_->GetPreferredSize().height());
335 y += newtab_button_->GetPreferredSize().height();
199 } 336 }
337
338 ideal_height_ = y - layout_rect.y() - first_tab_y_offset_;
339
340 scroll_up_button_->SetEnabled(first_tab_y_offset_ != 0);
341 scroll_down_button_->SetEnabled(GetMaxOffset() != 0 &&
342 first_tab_y_offset_ != GetMaxOffset());
200 } 343 }
201 344
202 void SideTabStrip::StartInsertTabAnimation(int model_index) { 345 void SideTabStrip::StartInsertTabAnimation(int model_index) {
203 PrepareForAnimation(); 346 PrepareForAnimation();
204 347
205 GenerateIdealBounds(); 348 GenerateIdealBounds();
206 349
207 int tab_data_index = ModelIndexToTabIndex(model_index); 350 int tab_data_index = ModelIndexToTabIndex(model_index);
208 BaseTab* tab = base_tab_at_tab_index(tab_data_index); 351 BaseTab* tab = base_tab_at_tab_index(tab_data_index);
209 if (model_index == 0) { 352 if (model_index == 0) {
(...skipping 15 matching lines...) Expand all
225 bounds_animator().AnimateViewTo(tab, ideal_bounds(i)); 368 bounds_animator().AnimateViewTo(tab, ideal_bounds(i));
226 } 369 }
227 370
228 bounds_animator().AnimateViewTo(newtab_button_, newtab_button_bounds_); 371 bounds_animator().AnimateViewTo(newtab_button_, newtab_button_bounds_);
229 372
230 bounds_animator().AnimateViewTo(separator_, separator_bounds_); 373 bounds_animator().AnimateViewTo(separator_, separator_bounds_);
231 } 374 }
232 375
233 void SideTabStrip::DoLayout() { 376 void SideTabStrip::DoLayout() {
234 BaseTabStrip::DoLayout(); 377 BaseTabStrip::DoLayout();
235
236 newtab_button_->SetBoundsRect(newtab_button_bounds_); 378 newtab_button_->SetBoundsRect(newtab_button_bounds_);
237
238 separator_->SetBoundsRect(separator_bounds_); 379 separator_->SetBoundsRect(separator_bounds_);
380 int scroll_button_y = height() - kScrollButtonHeight;
381 scroll_up_button_->SetBounds(0, scroll_button_y, width() / 2,
382 kScrollButtonHeight);
383 scroll_down_button_->SetBounds(width() / 2, scroll_button_y, width() / 2,
384 kScrollButtonHeight);
239 } 385 }
240 386
241 void SideTabStrip::LayoutDraggedTabsAt(const std::vector<BaseTab*>& tabs, 387 void SideTabStrip::LayoutDraggedTabsAt(const std::vector<BaseTab*>& tabs,
242 BaseTab* active_tab, 388 BaseTab* active_tab,
243 const gfx::Point& location, 389 const gfx::Point& location,
244 bool initial_drag) { 390 bool initial_drag) {
245 // TODO: add support for initial_drag (see TabStrip's implementation). 391 // TODO: add support for initial_drag (see TabStrip's implementation).
246 gfx::Rect layout_rect = GetContentsBounds(); 392 gfx::Rect layout_rect = GetContentsBounds();
247 layout_rect.Inset(kTabStripInset, kTabStripInset); 393 layout_rect.Inset(kTabStripInset, kTabStripInset);
248 int y = location.y(); 394 int y = location.y();
(...skipping 15 matching lines...) Expand all
264 BaseTab* tab = tabs[i]; 410 BaseTab* tab = tabs[i];
265 gfx::Rect bounds(tab->bounds()); 411 gfx::Rect bounds(tab->bounds());
266 bounds.set_y(y); 412 bounds.set_y(y);
267 y += tab->height(); 413 y += tab->height();
268 } 414 }
269 } 415 }
270 416
271 int SideTabStrip::GetSizeNeededForTabs(const std::vector<BaseTab*>& tabs) { 417 int SideTabStrip::GetSizeNeededForTabs(const std::vector<BaseTab*>& tabs) {
272 return static_cast<int>(tabs.size()) * SideTab::GetPreferredHeight(); 418 return static_cast<int>(tabs.size()) * SideTab::GetPreferredHeight();
273 } 419 }
420
421 void SideTabStrip::OnBoundsChanged(const gfx::Rect& previous_bounds) {
422 // When our height changes we may be able to show more.
423 first_tab_y_offset_ = std::max(GetMaxOffset(),
424 std::min(0, first_tab_y_offset_));
425 for (int i = 0; i < controller()->GetCount(); ++i) {
426 if (controller()->IsActiveTab(i)) {
427 MakeTabVisible(ModelIndexToTabIndex(i));
428 break;
429 }
430 }
431 }
432
433 void SideTabStrip::SetFirstTabYOffset(int new_offset) {
434 int max_offset = GetMaxOffset();
435 if (max_offset == 0) {
436 // All the tabs fit, no need to scroll.
437 return;
438 }
439 new_offset = std::max(max_offset, std::min(0, new_offset));
440 if (new_offset == first_tab_y_offset_)
441 return;
442
443 StopAnimating(false);
444 first_tab_y_offset_ = new_offset;
445 GenerateIdealBounds();
446 DoLayout();
447
448 }
449
450 int SideTabStrip::GetMaxOffset() const {
451 int available_height = GetMaxTabY() - kTabStripInset;
452 return std::min(0, available_height - ideal_height_);
453 }
454
455 int SideTabStrip::GetMaxTabY() const {
456 return height() - kTabStripInset - kScrollButtonVerticalPadding -
457 kScrollButtonHeight;
458 }
459
460 void SideTabStrip::MakeTabVisible(int tab_index) {
461 if (height() == 0)
462 return;
463
464 if (ideal_bounds(tab_index).y() < kTabStripInset) {
465 SetFirstTabYOffset(first_tab_y_offset_ - ideal_bounds(tab_index).y() +
466 kTabStripInset);
467 } else if (ideal_bounds(tab_index).bottom() > GetMaxTabY()) {
468 SetFirstTabYOffset(GetMaxTabY() - (ideal_bounds(tab_index).bottom() -
469 first_tab_y_offset_));
470 }
471 }
OLDNEW
« no previous file with comments | « chrome/browser/ui/views/tabs/side_tab_strip.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698