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

Side by Side Diff: ui/views/controls/tabbed_pane/tabbed_pane.cc

Issue 2368283002: views: add focus to TabbedPane (Closed)
Patch Set: Created 4 years, 2 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
« no previous file with comments | « ui/views/controls/tabbed_pane/tabbed_pane.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) 2012 The Chromium Authors. All rights reserved. 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 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 "ui/views/controls/tabbed_pane/tabbed_pane.h" 5 #include "ui/views/controls/tabbed_pane/tabbed_pane.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/macros.h" 8 #include "base/macros.h"
9 #include "third_party/skia/include/core/SkPaint.h" 9 #include "third_party/skia/include/core/SkPaint.h"
10 #include "third_party/skia/include/core/SkPath.h" 10 #include "third_party/skia/include/core/SkPath.h"
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
53 static const char kViewClassName[]; 53 static const char kViewClassName[];
54 54
55 Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents); 55 Tab(TabbedPane* tabbed_pane, const base::string16& title, View* contents);
56 ~Tab() override; 56 ~Tab() override;
57 57
58 View* contents() const { return contents_; } 58 View* contents() const { return contents_; }
59 59
60 bool selected() const { return contents_->visible(); } 60 bool selected() const { return contents_->visible(); }
61 void SetSelected(bool selected); 61 void SetSelected(bool selected);
62 62
63 // This is a bit odd: since Tabs are not actually focusable, but rather the
Evan Stade 2016/09/26 16:28:36 Why make the tabstrip focusable instead of the tab
Elly Fong-Jones 2016/09/26 17:48:36 I thought it would end up being sort of semantical
64 // containing TabStrip is, Tabs need to be told when the TabStrip gains or
65 // loses focus so they can swap to/from the focus ring border.
66 void OnContainerFocusChanged();
67
63 // Overridden from View: 68 // Overridden from View:
64 bool OnMousePressed(const ui::MouseEvent& event) override; 69 bool OnMousePressed(const ui::MouseEvent& event) override;
65 void OnMouseEntered(const ui::MouseEvent& event) override; 70 void OnMouseEntered(const ui::MouseEvent& event) override;
66 void OnMouseExited(const ui::MouseEvent& event) override; 71 void OnMouseExited(const ui::MouseEvent& event) override;
67 void OnGestureEvent(ui::GestureEvent* event) override; 72 void OnGestureEvent(ui::GestureEvent* event) override;
68 gfx::Size GetPreferredSize() const override; 73 gfx::Size GetPreferredSize() const override;
69 void Layout() override; 74 void Layout() override;
70 const char* GetClassName() const override; 75 const char* GetClassName() const override;
71 76
72 protected: 77 protected:
73 Label* title() { return title_; } 78 Label* title() { return title_; }
74 79
75 // Called whenever |tab_state_| changes. 80 // Called whenever |tab_state_| changes.
76 virtual void OnStateChanged(); 81 virtual void OnStateChanged();
77 82
83 // Returns whether the containing TabStrip has focus.
84 bool ContainerHasFocus();
85
78 private: 86 private:
79 enum TabState { 87 enum TabState {
80 TAB_INACTIVE, 88 TAB_INACTIVE,
81 TAB_ACTIVE, 89 TAB_ACTIVE,
82 TAB_HOVERED, 90 TAB_HOVERED,
83 }; 91 };
84 92
85 void SetState(TabState tab_state); 93 void SetState(TabState tab_state);
86 94
87 TabbedPane* tabbed_pane_; 95 TabbedPane* tabbed_pane_;
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 } 253 }
246 254
247 void Tab::SetState(TabState tab_state) { 255 void Tab::SetState(TabState tab_state) {
248 if (tab_state == tab_state_) 256 if (tab_state == tab_state_)
249 return; 257 return;
250 tab_state_ = tab_state; 258 tab_state_ = tab_state;
251 OnStateChanged(); 259 OnStateChanged();
252 SchedulePaint(); 260 SchedulePaint();
253 } 261 }
254 262
263 bool Tab::ContainerHasFocus() {
264 return tabbed_pane_->HasFocus();
265 }
266
267 void Tab::OnContainerFocusChanged() {
268 OnStateChanged();
269 SchedulePaint();
270 }
271
272 // The border used for MdTabs when they are both focused and selected: a
273 // rectangle with the bottom side in a base color and the other sides in a
274 // lightened version of the base color.
275 class MdTabFocusRingBorder : public Border {
276 public:
277 MdTabFocusRingBorder(SkColor base_color);
Evan Stade 2016/09/26 16:28:35 why pass in a parameter? Why not view.GetNativeThe
Elly Fong-Jones 2016/09/26 17:48:36 Oh good call, reaching into the View did not occur
278 ~MdTabFocusRingBorder() override;
279
280 private:
281 void Paint(const View& view, gfx::Canvas* canvas) override;
282 gfx::Insets GetInsets() const override;
283 gfx::Size GetMinimumSize() const override;
284
285 SkColor base_color_;
286 };
287
288 MdTabFocusRingBorder::MdTabFocusRingBorder(SkColor base_color)
289 : base_color_(base_color) {}
290 MdTabFocusRingBorder::~MdTabFocusRingBorder() {}
291
292 void MdTabFocusRingBorder::Paint(const View& view, gfx::Canvas* canvas) {
293 gfx::Insets insets = GetInsets();
294 // TODO(ellyjones): should this 0x66 be part of NativeTheme somehow?
295 SkColor light_color = SkColorSetA(base_color_, 0x66);
296
297 SkPaint paint;
298 paint.setColor(light_color);
299 paint.setStyle(SkPaint::kStroke_Style);
300 paint.setStrokeWidth(2);
301
302 gfx::Rect bounds = gfx::Rect(0, 0, view.width(), view.height());
303 bounds.Inset(1, 1);
Evan Stade 2016/09/26 16:28:36 can this instead be stroke width / 2? Makes it mor
Elly Fong-Jones 2016/09/26 17:48:36 Done.
304
305 // Draw the lighter-colored stroke first, then draw the heavier stroke over
306 // the bottom of it. This is fine because the heavier stroke has 1.0 alpha, so
307 // the lighter stroke won't show through.
308 canvas->DrawRect(bounds, paint);
309 canvas->FillRect(gfx::Rect(0, view.height() - insets.bottom(), view.width(),
310 insets.bottom()),
311 base_color_);
312 }
313
314 gfx::Insets MdTabFocusRingBorder::GetInsets() const {
315 return gfx::Insets(2, 2);
316 }
317
318 gfx::Size MdTabFocusRingBorder::GetMinimumSize() const {
319 return gfx::Size(4, 4);
Evan Stade 2016/09/26 16:28:35 Is this necessary?
Elly Fong-Jones 2016/09/26 17:48:36 It seems to be - GetMinimumSize() is pure virtual
320 }
321
255 MdTab::MdTab(TabbedPane* tabbed_pane, 322 MdTab::MdTab(TabbedPane* tabbed_pane,
256 const base::string16& title, 323 const base::string16& title,
257 View* contents) 324 View* contents)
258 : Tab(tabbed_pane, title, contents) { 325 : Tab(tabbed_pane, title, contents) {
259 OnStateChanged(); 326 OnStateChanged();
260 } 327 }
261 328
262 MdTab::~MdTab() {} 329 MdTab::~MdTab() {}
263 330
264 void MdTab::OnStateChanged() { 331 void MdTab::OnStateChanged() {
265 ui::NativeTheme* theme = GetNativeTheme(); 332 ui::NativeTheme* theme = GetNativeTheme();
266 SkColor border_color = theme->GetSystemColor( 333 SkColor border_color = theme->GetSystemColor(
267 selected() ? ui::NativeTheme::kColorId_FocusedBorderColor 334 selected() ? ui::NativeTheme::kColorId_FocusedBorderColor
268 : ui::NativeTheme::kColorId_UnfocusedBorderColor); 335 : ui::NativeTheme::kColorId_UnfocusedBorderColor);
269 int border_thickness = selected() ? 2 : 1; 336 int border_thickness = selected() ? 2 : 1;
Evan Stade 2016/09/26 16:28:35 seems a little weird that the border thickness dep
Elly Fong-Jones 2016/09/26 17:48:36 It does not seem to. It *is* a little weird - mayb
270 SetBorder( 337 if (ContainerHasFocus() && selected()) {
271 Border::CreateSolidSidedBorder(0, 0, border_thickness, 0, border_color)); 338 SetBorder(
339 base::MakeUnique<MdTabFocusRingBorder>(GetNativeTheme()->GetSystemColor(
340 ui::NativeTheme::kColorId_FocusedBorderColor)));
341 } else {
342 SetBorder(Border::CreateSolidSidedBorder(0, 0, border_thickness, 0,
343 border_color));
344 }
272 345
273 SkColor font_color = selected() 346 SkColor font_color = selected()
274 ? theme->GetSystemColor(ui::NativeTheme::kColorId_CallToActionColor) 347 ? theme->GetSystemColor(ui::NativeTheme::kColorId_CallToActionColor)
275 : theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonEnabledColor); 348 : theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonEnabledColor);
276 title()->SetEnabledColor(font_color); 349 title()->SetEnabledColor(font_color);
277 350
278 gfx::Font::Weight font_weight = gfx::Font::Weight::MEDIUM; 351 gfx::Font::Weight font_weight = gfx::Font::Weight::MEDIUM;
279 #if defined(OS_WIN) 352 #if defined(OS_WIN)
280 if (selected()) 353 if (selected())
281 font_weight = gfx::Font::Weight::BOLD; 354 font_weight = gfx::Font::Weight::BOLD;
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
396 AddChildView(contents_); 469 AddChildView(contents_);
397 } 470 }
398 471
399 TabbedPane::~TabbedPane() {} 472 TabbedPane::~TabbedPane() {}
400 473
401 int TabbedPane::GetTabCount() { 474 int TabbedPane::GetTabCount() {
402 DCHECK_EQ(tab_strip_->child_count(), contents_->child_count()); 475 DCHECK_EQ(tab_strip_->child_count(), contents_->child_count());
403 return contents_->child_count(); 476 return contents_->child_count();
404 } 477 }
405 478
406 View* TabbedPane::GetSelectedTab() {
407 return selected_tab_index() < 0 ?
408 NULL : GetTabAt(selected_tab_index())->contents();
409 }
410
411 void TabbedPane::AddTab(const base::string16& title, View* contents) { 479 void TabbedPane::AddTab(const base::string16& title, View* contents) {
412 AddTabAtIndex(tab_strip_->child_count(), title, contents); 480 AddTabAtIndex(tab_strip_->child_count(), title, contents);
413 } 481 }
414 482
415 void TabbedPane::AddTabAtIndex(int index, 483 void TabbedPane::AddTabAtIndex(int index,
416 const base::string16& title, 484 const base::string16& title,
417 View* contents) { 485 View* contents) {
418 DCHECK(index >= 0 && index <= GetTabCount()); 486 DCHECK(index >= 0 && index <= GetTabCount());
419 contents->SetVisible(false); 487 contents->SetVisible(false);
420 488
421 tab_strip_->AddChildViewAt( 489 tab_strip_->AddChildViewAt(
422 ui::MaterialDesignController::IsSecondaryUiMaterial() 490 ui::MaterialDesignController::IsSecondaryUiMaterial()
423 ? new MdTab(this, title, contents) 491 ? new MdTab(this, title, contents)
424 : new Tab(this, title, contents), 492 : new Tab(this, title, contents),
425 index); 493 index);
426 contents_->AddChildViewAt(contents, index); 494 contents_->AddChildViewAt(contents, index);
427 if (selected_tab_index() < 0) 495 if (selected_tab_index() < 0)
428 SelectTabAt(index); 496 SelectTabAt(index);
429 497
430 PreferredSizeChanged(); 498 PreferredSizeChanged();
431 } 499 }
432 500
433 void TabbedPane::SelectTabAt(int index) { 501 void TabbedPane::SelectTabAt(int index) {
434 DCHECK(index >= 0 && index < GetTabCount()); 502 DCHECK(index >= 0 && index < GetTabCount());
435 if (index == selected_tab_index()) 503 if (index == selected_tab_index())
436 return; 504 return;
437 505
438 if (selected_tab_index() >= 0) 506 if (GetSelectedTab())
439 GetTabAt(selected_tab_index())->SetSelected(false); 507 GetSelectedTab()->SetSelected(false);
440 508
441 selected_tab_index_ = index; 509 selected_tab_index_ = index;
442 Tab* tab = GetTabAt(index); 510 Tab* tab = GetTabAt(index);
443 tab->SetSelected(true); 511 tab->SetSelected(true);
444 tab_strip_->SchedulePaint(); 512 tab_strip_->SchedulePaint();
445 513
446 FocusManager* focus_manager = tab->contents()->GetFocusManager(); 514 FocusManager* focus_manager = tab->contents()->GetFocusManager();
447 if (focus_manager) { 515 if (focus_manager) {
448 const View* focused_view = focus_manager->GetFocusedView(); 516 const View* focused_view = focus_manager->GetFocusedView();
449 if (focused_view && contents_->Contains(focused_view) && 517 if (focused_view && contents_->Contains(focused_view) &&
(...skipping 16 matching lines...) Expand all
466 for (int i = 0; i < contents_->child_count(); ++i) 534 for (int i = 0; i < contents_->child_count(); ++i)
467 size.SetToMax(contents_->child_at(i)->GetPreferredSize()); 535 size.SetToMax(contents_->child_at(i)->GetPreferredSize());
468 size.Enlarge(0, tab_strip_->GetPreferredSize().height()); 536 size.Enlarge(0, tab_strip_->GetPreferredSize().height());
469 return size; 537 return size;
470 } 538 }
471 539
472 Tab* TabbedPane::GetTabAt(int index) { 540 Tab* TabbedPane::GetTabAt(int index) {
473 return static_cast<Tab*>(tab_strip_->child_at(index)); 541 return static_cast<Tab*>(tab_strip_->child_at(index));
474 } 542 }
475 543
544 Tab* TabbedPane::GetSelectedTab() {
545 return selected_tab_index() >= 0 ? GetTabAt(selected_tab_index()) : nullptr;
546 }
547
548 bool TabbedPane::MoveSelectionBy(int delta) {
549 const int tab_count = GetTabCount();
550 if (tab_count <= 1)
551 return false;
552 int next_selected_index = (selected_tab_index() + delta) % tab_count;
553 if (next_selected_index < 0)
554 next_selected_index += tab_count;
555 SelectTabAt(next_selected_index);
556 return true;
557 }
558
476 void TabbedPane::Layout() { 559 void TabbedPane::Layout() {
477 const gfx::Size size = tab_strip_->GetPreferredSize(); 560 const gfx::Size size = tab_strip_->GetPreferredSize();
478 tab_strip_->SetBounds(0, 0, width(), size.height()); 561 tab_strip_->SetBounds(0, 0, width(), size.height());
479 contents_->SetBounds(0, tab_strip_->bounds().bottom(), width(), 562 contents_->SetBounds(0, tab_strip_->bounds().bottom(), width(),
480 std::max(0, height() - size.height())); 563 std::max(0, height() - size.height()));
481 for (int i = 0; i < contents_->child_count(); ++i) 564 for (int i = 0; i < contents_->child_count(); ++i)
482 contents_->child_at(i)->SetSize(contents_->size()); 565 contents_->child_at(i)->SetSize(contents_->size());
483 } 566 }
484 567
485 void TabbedPane::ViewHierarchyChanged( 568 void TabbedPane::ViewHierarchyChanged(
486 const ViewHierarchyChangedDetails& details) { 569 const ViewHierarchyChangedDetails& details) {
487 if (details.is_add) { 570 if (details.is_add) {
488 // Support navigating tabs by Ctrl+Tab and Ctrl+Shift+Tab. 571 // Support navigating tabs by Ctrl+Tab and Ctrl+Shift+Tab.
489 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, 572 AddAccelerator(ui::Accelerator(ui::VKEY_TAB,
490 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)); 573 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN));
491 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN)); 574 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN));
492 } 575 }
493 } 576 }
494 577
495 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { 578 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) {
496 // Handle Ctrl+Tab and Ctrl+Shift+Tab navigation of pages. 579 // Handle Ctrl+Tab and Ctrl+Shift+Tab navigation of pages.
497 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); 580 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown());
498 const int tab_count = GetTabCount(); 581 return MoveSelectionBy(accelerator.IsShiftDown() ? -1 : 1);
499 if (tab_count <= 1) 582 }
583
584 bool TabbedPane::OnKeyPressed(const ui::KeyEvent& event) {
585 if (!HasFocus())
500 return false; 586 return false;
501 const int increment = accelerator.IsShiftDown() ? -1 : 1; 587 ui::DomKey key = event.GetDomKey();
502 int next_tab_index = (selected_tab_index() + increment) % tab_count; 588 if (key != ui::DomKey::ARROW_LEFT && key != ui::DomKey::ARROW_RIGHT)
503 // Wrap around. 589 return false;
504 if (next_tab_index < 0) 590 return MoveSelectionBy(key == ui::DomKey::ARROW_LEFT ? 1 : -1);
505 next_tab_index += tab_count;
506 SelectTabAt(next_tab_index);
507 return true;
508 } 591 }
509 592
510 const char* TabbedPane::GetClassName() const { 593 const char* TabbedPane::GetClassName() const {
511 return kViewClassName; 594 return kViewClassName;
512 } 595 }
513 596
514 void TabbedPane::OnFocus() { 597 void TabbedPane::OnFocus() {
515 View::OnFocus(); 598 View::OnFocus();
516 599
517 View* selected_tab = GetSelectedTab(); 600 Tab* selected = GetSelectedTab();
518 if (selected_tab) { 601 if (selected) {
519 selected_tab->NotifyAccessibilityEvent( 602 selected->OnContainerFocusChanged();
520 ui::AX_EVENT_FOCUS, true); 603 if (selected->contents())
604 selected->contents()->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
521 } 605 }
522 } 606 }
523 607
608 void TabbedPane::OnBlur() {
609 View::OnBlur();
610 Tab* selected_tab = GetSelectedTab();
611 if (selected_tab)
612 selected_tab->OnContainerFocusChanged();
613 }
614
524 void TabbedPane::GetAccessibleState(ui::AXViewState* state) { 615 void TabbedPane::GetAccessibleState(ui::AXViewState* state) {
525 state->role = ui::AX_ROLE_TAB_LIST; 616 state->role = ui::AX_ROLE_TAB_LIST;
526 } 617 }
527 618
528 } // namespace views 619 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/tabbed_pane/tabbed_pane.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698