Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / views / controls / tabbed_pane / tabbed_pane.cc
blobbbf341b3c86bbd0fc2d95c65d226305d8de33a73
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/views/controls/tabbed_pane/tabbed_pane.h"
7 #include "base/logging.h"
8 #include "ui/accessibility/ax_view_state.h"
9 #include "ui/base/resource/resource_bundle.h"
10 #include "ui/events/keycodes/keyboard_codes.h"
11 #include "ui/gfx/canvas.h"
12 #include "ui/gfx/font_list.h"
13 #include "ui/views/controls/label.h"
14 #include "ui/views/controls/tabbed_pane/tabbed_pane_listener.h"
15 #include "ui/views/layout/layout_manager.h"
16 #include "ui/views/widget/widget.h"
18 namespace {
20 // TODO(markusheintz|msw): Use NativeTheme colors.
21 const SkColor kTabTitleColor_Inactive = SkColorSetRGB(0x64, 0x64, 0x64);
22 const SkColor kTabTitleColor_Active = SK_ColorBLACK;
23 const SkColor kTabTitleColor_Hovered = SK_ColorBLACK;
24 const SkColor kTabBorderColor = SkColorSetRGB(0xC8, 0xC8, 0xC8);
25 const SkScalar kTabBorderThickness = 1.0f;
27 } // namespace
29 namespace views {
31 // static
32 const char TabbedPane::kViewClassName[] = "TabbedPane";
34 // The tab view shown in the tab strip.
35 class Tab : public View {
36 public:
37 // Internal class name.
38 static const char kViewClassName[];
40 Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents);
41 ~Tab() override;
43 View* contents() const { return contents_; }
45 bool selected() const { return contents_->visible(); }
46 void SetSelected(bool selected);
48 // Overridden from View:
49 bool OnMousePressed(const ui::MouseEvent& event) override;
50 void OnMouseEntered(const ui::MouseEvent& event) override;
51 void OnMouseExited(const ui::MouseEvent& event) override;
52 void OnGestureEvent(ui::GestureEvent* event) override;
53 gfx::Size GetPreferredSize() const override;
54 void Layout() override;
55 const char* GetClassName() const override;
57 private:
58 enum TabState {
59 TAB_INACTIVE,
60 TAB_ACTIVE,
61 TAB_HOVERED,
64 void SetState(TabState tab_state);
66 TabbedPane* tabbed_pane_;
67 Label* title_;
68 gfx::Size preferred_title_size_;
69 TabState tab_state_;
70 // The content view associated with this tab.
71 View* contents_;
73 DISALLOW_COPY_AND_ASSIGN(Tab);
76 // The tab strip shown above the tab contents.
77 class TabStrip : public View {
78 public:
79 // Internal class name.
80 static const char kViewClassName[];
82 explicit TabStrip(TabbedPane* tabbed_pane);
83 ~TabStrip() override;
85 // Overridden from View:
86 gfx::Size GetPreferredSize() const override;
87 void Layout() override;
88 const char* GetClassName() const override;
89 void OnPaint(gfx::Canvas* canvas) override;
91 private:
92 TabbedPane* tabbed_pane_;
94 DISALLOW_COPY_AND_ASSIGN(TabStrip);
97 // static
98 const char Tab::kViewClassName[] = "Tab";
100 Tab::Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents)
101 : tabbed_pane_(tabbed_pane),
102 title_(new Label(title,
103 ui::ResourceBundle::GetSharedInstance().GetFontList(
104 ui::ResourceBundle::BoldFont))),
105 tab_state_(TAB_ACTIVE),
106 contents_(contents) {
107 // Calculate this now while the font list is guaranteed to be bold.
108 preferred_title_size_ = title_->GetPreferredSize();
110 SetState(TAB_INACTIVE);
111 AddChildView(title_);
114 Tab::~Tab() {}
116 void Tab::SetSelected(bool selected) {
117 contents_->SetVisible(selected);
118 SetState(selected ? TAB_ACTIVE : TAB_INACTIVE);
121 bool Tab::OnMousePressed(const ui::MouseEvent& event) {
122 if (event.IsOnlyLeftMouseButton() &&
123 GetLocalBounds().Contains(event.location()))
124 tabbed_pane_->SelectTab(this);
125 return true;
128 void Tab::OnMouseEntered(const ui::MouseEvent& event) {
129 SetState(selected() ? TAB_ACTIVE : TAB_HOVERED);
132 void Tab::OnMouseExited(const ui::MouseEvent& event) {
133 SetState(selected() ? TAB_ACTIVE : TAB_INACTIVE);
136 void Tab::OnGestureEvent(ui::GestureEvent* event) {
137 switch (event->type()) {
138 case ui::ET_GESTURE_TAP_DOWN:
139 // Fallthrough.
140 case ui::ET_GESTURE_TAP:
141 // SelectTab also sets the right tab color.
142 tabbed_pane_->SelectTab(this);
143 break;
144 case ui::ET_GESTURE_TAP_CANCEL:
145 SetState(selected() ? TAB_ACTIVE : TAB_INACTIVE);
146 break;
147 default:
148 break;
150 event->SetHandled();
153 gfx::Size Tab::GetPreferredSize() const {
154 gfx::Size size(preferred_title_size_);
155 size.Enlarge(21, 9);
156 const int kTabMinWidth = 54;
157 if (size.width() < kTabMinWidth)
158 size.set_width(kTabMinWidth);
159 return size;
162 void Tab::Layout() {
163 gfx::Rect bounds = GetLocalBounds();
164 bounds.Inset(0, 1, 0, 0);
165 bounds.ClampToCenteredSize(preferred_title_size_);
166 title_->SetBoundsRect(bounds);
169 const char* Tab::GetClassName() const {
170 return kViewClassName;
173 void Tab::SetState(TabState tab_state) {
174 if (tab_state == tab_state_)
175 return;
176 tab_state_ = tab_state;
178 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
179 switch (tab_state) {
180 case TAB_INACTIVE:
181 title_->SetEnabledColor(kTabTitleColor_Inactive);
182 title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BaseFont));
183 break;
184 case TAB_ACTIVE:
185 title_->SetEnabledColor(kTabTitleColor_Active);
186 title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BoldFont));
187 break;
188 case TAB_HOVERED:
189 title_->SetEnabledColor(kTabTitleColor_Hovered);
190 title_->SetFontList(rb.GetFontList(ui::ResourceBundle::BaseFont));
191 break;
193 SchedulePaint();
196 // static
197 const char TabStrip::kViewClassName[] = "TabStrip";
199 TabStrip::TabStrip(TabbedPane* tabbed_pane) : tabbed_pane_(tabbed_pane) {}
201 TabStrip::~TabStrip() {}
203 gfx::Size TabStrip::GetPreferredSize() const {
204 gfx::Size size;
205 for (int i = 0; i < child_count(); ++i) {
206 const gfx::Size child_size = child_at(i)->GetPreferredSize();
207 size.SetSize(size.width() + child_size.width(),
208 std::max(size.height(), child_size.height()));
210 return size;
213 void TabStrip::Layout() {
214 const int kTabOffset = 9;
215 int x = kTabOffset; // Layout tabs with an offset to the tabstrip border.
216 for (int i = 0; i < child_count(); ++i) {
217 gfx::Size ps = child_at(i)->GetPreferredSize();
218 child_at(i)->SetBounds(x, 0, ps.width(), ps.height());
219 x = child_at(i)->bounds().right();
223 const char* TabStrip::GetClassName() const {
224 return kViewClassName;
227 void TabStrip::OnPaint(gfx::Canvas* canvas) {
228 OnPaintBackground(canvas);
230 // Draw the TabStrip border.
231 SkPaint paint;
232 paint.setColor(kTabBorderColor);
233 paint.setStrokeWidth(kTabBorderThickness);
234 SkScalar line_y = SkIntToScalar(height()) - (kTabBorderThickness / 2);
235 SkScalar line_end = SkIntToScalar(width());
236 int selected_tab_index = tabbed_pane_->selected_tab_index();
237 if (selected_tab_index >= 0) {
238 Tab* selected_tab = tabbed_pane_->GetTabAt(selected_tab_index);
239 SkPath path;
240 SkScalar tab_height =
241 SkIntToScalar(selected_tab->height()) - kTabBorderThickness;
242 SkScalar tab_width =
243 SkIntToScalar(selected_tab->width()) - kTabBorderThickness;
244 SkScalar tab_start = SkIntToScalar(selected_tab->GetMirroredX());
245 path.moveTo(0, line_y);
246 path.rLineTo(tab_start, 0);
247 path.rLineTo(0, -tab_height);
248 path.rLineTo(tab_width, 0);
249 path.rLineTo(0, tab_height);
250 path.lineTo(line_end, line_y);
252 SkPaint paint;
253 paint.setStyle(SkPaint::kStroke_Style);
254 paint.setColor(kTabBorderColor);
255 paint.setStrokeWidth(kTabBorderThickness);
256 canvas->DrawPath(path, paint);
257 } else {
258 canvas->sk_canvas()->drawLine(0, line_y, line_end, line_y, paint);
262 TabbedPane::TabbedPane()
263 : listener_(NULL),
264 tab_strip_(new TabStrip(this)),
265 contents_(new View()),
266 selected_tab_index_(-1) {
267 SetFocusable(true);
268 AddChildView(tab_strip_);
269 AddChildView(contents_);
272 TabbedPane::~TabbedPane() {}
274 int TabbedPane::GetTabCount() {
275 DCHECK_EQ(tab_strip_->child_count(), contents_->child_count());
276 return contents_->child_count();
279 View* TabbedPane::GetSelectedTab() {
280 return selected_tab_index() < 0 ?
281 NULL : GetTabAt(selected_tab_index())->contents();
284 void TabbedPane::AddTab(const base::string16& title, View* contents) {
285 AddTabAtIndex(tab_strip_->child_count(), title, contents);
288 void TabbedPane::AddTabAtIndex(int index,
289 const base::string16& title,
290 View* contents) {
291 DCHECK(index >= 0 && index <= GetTabCount());
292 contents->SetVisible(false);
294 tab_strip_->AddChildViewAt(new Tab(this, title, contents), index);
295 contents_->AddChildViewAt(contents, index);
296 if (selected_tab_index() < 0)
297 SelectTabAt(index);
299 PreferredSizeChanged();
302 void TabbedPane::SelectTabAt(int index) {
303 DCHECK(index >= 0 && index < GetTabCount());
304 if (index == selected_tab_index())
305 return;
307 if (selected_tab_index() >= 0)
308 GetTabAt(selected_tab_index())->SetSelected(false);
310 selected_tab_index_ = index;
311 Tab* tab = GetTabAt(index);
312 tab->SetSelected(true);
313 tab_strip_->SchedulePaint();
315 FocusManager* focus_manager = tab->contents()->GetFocusManager();
316 if (focus_manager) {
317 const View* focused_view = focus_manager->GetFocusedView();
318 if (focused_view && contents_->Contains(focused_view) &&
319 !tab->contents()->Contains(focused_view))
320 focus_manager->SetFocusedView(tab->contents());
323 if (listener())
324 listener()->TabSelectedAt(index);
327 void TabbedPane::SelectTab(Tab* tab) {
328 const int index = tab_strip_->GetIndexOf(tab);
329 if (index >= 0)
330 SelectTabAt(index);
333 gfx::Size TabbedPane::GetPreferredSize() const {
334 gfx::Size size;
335 for (int i = 0; i < contents_->child_count(); ++i)
336 size.SetToMax(contents_->child_at(i)->GetPreferredSize());
337 size.Enlarge(0, tab_strip_->GetPreferredSize().height());
338 return size;
341 Tab* TabbedPane::GetTabAt(int index) {
342 return static_cast<Tab*>(tab_strip_->child_at(index));
345 void TabbedPane::Layout() {
346 const gfx::Size size = tab_strip_->GetPreferredSize();
347 tab_strip_->SetBounds(0, 0, width(), size.height());
348 contents_->SetBounds(0, tab_strip_->bounds().bottom(), width(),
349 std::max(0, height() - size.height()));
350 for (int i = 0; i < contents_->child_count(); ++i)
351 contents_->child_at(i)->SetSize(contents_->size());
354 void TabbedPane::ViewHierarchyChanged(
355 const ViewHierarchyChangedDetails& details) {
356 if (details.is_add) {
357 // Support navigating tabs by Ctrl+Tab and Ctrl+Shift+Tab.
358 AddAccelerator(ui::Accelerator(ui::VKEY_TAB,
359 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN));
360 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN));
364 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) {
365 // Handle Ctrl+Tab and Ctrl+Shift+Tab navigation of pages.
366 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown());
367 const int tab_count = GetTabCount();
368 if (tab_count <= 1)
369 return false;
370 const int increment = accelerator.IsShiftDown() ? -1 : 1;
371 int next_tab_index = (selected_tab_index() + increment) % tab_count;
372 // Wrap around.
373 if (next_tab_index < 0)
374 next_tab_index += tab_count;
375 SelectTabAt(next_tab_index);
376 return true;
379 const char* TabbedPane::GetClassName() const {
380 return kViewClassName;
383 void TabbedPane::OnFocus() {
384 View::OnFocus();
386 View* selected_tab = GetSelectedTab();
387 if (selected_tab) {
388 selected_tab->NotifyAccessibilityEvent(
389 ui::AX_EVENT_FOCUS, true);
393 void TabbedPane::GetAccessibleState(ui::AXViewState* state) {
394 state->role = ui::AX_ROLE_TAB_LIST;
397 } // namespace views