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

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

Issue 2368283002: views: add focus to TabbedPane (Closed)
Patch Set: remove OnContainerFocusChanged 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 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
61 void SetSelected(bool selected); 61 void SetSelected(bool selected);
62 62
63 // Overridden from View: 63 // Overridden from View:
64 bool OnMousePressed(const ui::MouseEvent& event) override; 64 bool OnMousePressed(const ui::MouseEvent& event) override;
65 void OnMouseEntered(const ui::MouseEvent& event) override; 65 void OnMouseEntered(const ui::MouseEvent& event) override;
66 void OnMouseExited(const ui::MouseEvent& event) override; 66 void OnMouseExited(const ui::MouseEvent& event) override;
67 void OnGestureEvent(ui::GestureEvent* event) override; 67 void OnGestureEvent(ui::GestureEvent* event) override;
68 gfx::Size GetPreferredSize() const override; 68 gfx::Size GetPreferredSize() const override;
69 void Layout() override; 69 void Layout() override;
70 const char* GetClassName() const override; 70 const char* GetClassName() const override;
71 void OnFocus() override;
72 void OnBlur() override;
71 73
72 protected: 74 protected:
73 Label* title() { return title_; } 75 Label* title() { return title_; }
74 76
75 // Called whenever |tab_state_| changes. 77 // Called whenever |tab_state_| changes.
76 virtual void OnStateChanged(); 78 virtual void OnStateChanged();
77 79
80 // Returns whether the containing TabStrip has focus.
81 bool ContainerHasFocus();
82
78 private: 83 private:
79 enum TabState { 84 enum TabState {
80 TAB_INACTIVE, 85 TAB_INACTIVE,
81 TAB_ACTIVE, 86 TAB_ACTIVE,
82 TAB_HOVERED, 87 TAB_HOVERED,
83 }; 88 };
84 89
85 void SetState(TabState tab_state); 90 void SetState(TabState tab_state);
86 91
87 TabbedPane* tabbed_pane_; 92 TabbedPane* tabbed_pane_;
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
162 167
163 SetState(TAB_INACTIVE); 168 SetState(TAB_INACTIVE);
164 AddChildView(title_); 169 AddChildView(title_);
165 } 170 }
166 171
167 Tab::~Tab() {} 172 Tab::~Tab() {}
168 173
169 void Tab::SetSelected(bool selected) { 174 void Tab::SetSelected(bool selected) {
170 contents_->SetVisible(selected); 175 contents_->SetVisible(selected);
171 SetState(selected ? TAB_ACTIVE : TAB_INACTIVE); 176 SetState(selected ? TAB_ACTIVE : TAB_INACTIVE);
177 #if defined(OS_MACOSX)
178 SetFocusBehavior(selected ? FocusBehavior::ACCESSIBLE_ONLY
179 : FocusBehavior::NEVER);
180 #else
181 SetFocusBehavior(selected ? FocusBehavior::ALWAYS : FocusBehavior::NEVER);
Evan Stade 2016/09/28 21:25:54 Button::SetFocusForPlatform has to do something si
Elly Fong-Jones 2016/09/30 11:54:07 From a cursory look, it seems that ACCESSIBLE_ONLY
Evan Stade 2016/09/30 17:24:41 hmm, don't you mean to always use the non-osx code
182 #endif
172 } 183 }
173 184
174 void Tab::OnStateChanged() { 185 void Tab::OnStateChanged() {
175 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 186 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
176 switch (tab_state_) { 187 switch (tab_state_) {
177 case TAB_INACTIVE: 188 case TAB_INACTIVE:
178 title_->SetEnabledColor(kTabTitleColor_Inactive); 189 title_->SetEnabledColor(kTabTitleColor_Inactive);
179 title_->SetFontList(rb.GetFontListWithDelta( 190 title_->SetFontList(rb.GetFontListWithDelta(
180 ui::kLabelFontSizeDelta, gfx::Font::NORMAL, kInactiveWeight)); 191 ui::kLabelFontSizeDelta, gfx::Font::NORMAL, kInactiveWeight));
181 break; 192 break;
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 } 256 }
246 257
247 void Tab::SetState(TabState tab_state) { 258 void Tab::SetState(TabState tab_state) {
248 if (tab_state == tab_state_) 259 if (tab_state == tab_state_)
249 return; 260 return;
250 tab_state_ = tab_state; 261 tab_state_ = tab_state;
251 OnStateChanged(); 262 OnStateChanged();
252 SchedulePaint(); 263 SchedulePaint();
253 } 264 }
254 265
266 bool Tab::ContainerHasFocus() {
267 return tabbed_pane_->HasFocus();
268 }
269
270 void Tab::OnFocus() {
271 OnStateChanged();
272 if (contents())
273 contents()->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true);
274 SchedulePaint();
275 }
276
277 void Tab::OnBlur() {
278 OnStateChanged();
279 SchedulePaint();
280 }
281
282 // The border used for MdTabs when they are both focused and selected: a
283 // rectangle with the bottom side in a base color and the other sides in a
284 // lightened version of the base color.
285 class MdTabFocusRingBorder : public Border {
286 public:
287 MdTabFocusRingBorder();
288 ~MdTabFocusRingBorder() override;
289
290 private:
291 void Paint(const View& view, gfx::Canvas* canvas) override;
292 gfx::Insets GetInsets() const override;
293 gfx::Size GetMinimumSize() const override;
294 };
295
296 MdTabFocusRingBorder::MdTabFocusRingBorder() {}
297 MdTabFocusRingBorder::~MdTabFocusRingBorder() {}
298
299 void MdTabFocusRingBorder::Paint(const View& view, gfx::Canvas* canvas) {
300 gfx::Insets insets = GetInsets();
301 // TODO(ellyjones): should this 0x66 be part of NativeTheme somehow?
302 SkColor base_color = view.GetNativeTheme()->GetSystemColor(
303 ui::NativeTheme::kColorId_FocusedBorderColor);
304 SkColor light_color = SkColorSetA(base_color, 0x66);
305 const int kBorderStrokeWidth = 2;
306
307 SkPaint paint;
308 paint.setColor(light_color);
309 paint.setStyle(SkPaint::kStroke_Style);
310 paint.setStrokeWidth(kBorderStrokeWidth);
311
312 gfx::Rect bounds = gfx::Rect(0, 0, view.width(), view.height());
Evan Stade 2016/09/28 21:25:54 this is equivalent to view.GetLocalBounds()
Elly Fong-Jones 2016/09/30 11:54:07 Done.
313 bounds.Inset(kBorderStrokeWidth / 2, kBorderStrokeWidth / 2);
314
315 // Draw the lighter-colored stroke first, then draw the heavier stroke over
316 // the bottom of it. This is fine because the heavier stroke has 1.0 alpha, so
317 // the lighter stroke won't show through.
318 canvas->DrawRect(bounds, paint);
319 canvas->FillRect(gfx::Rect(0, view.height() - insets.bottom(), view.width(),
320 insets.bottom()),
321 base_color);
322 }
323
324 gfx::Insets MdTabFocusRingBorder::GetInsets() const {
325 return gfx::Insets(2, 2);
Evan Stade 2016/09/28 21:25:54 this 2 is the same as kBorderStrokeWidth, no? als
Elly Fong-Jones 2016/09/30 11:54:07 Done.
326 }
327
328 gfx::Size MdTabFocusRingBorder::GetMinimumSize() const {
329 return gfx::Size(GetInsets().width(), GetInsets().height());
330 }
331
255 MdTab::MdTab(TabbedPane* tabbed_pane, 332 MdTab::MdTab(TabbedPane* tabbed_pane,
256 const base::string16& title, 333 const base::string16& title,
257 View* contents) 334 View* contents)
258 : Tab(tabbed_pane, title, contents) { 335 : Tab(tabbed_pane, title, contents) {
259 OnStateChanged(); 336 OnStateChanged();
260 } 337 }
261 338
262 MdTab::~MdTab() {} 339 MdTab::~MdTab() {}
263 340
264 void MdTab::OnStateChanged() { 341 void MdTab::OnStateChanged() {
265 ui::NativeTheme* theme = GetNativeTheme(); 342 ui::NativeTheme* theme = GetNativeTheme();
266 SkColor border_color = theme->GetSystemColor( 343 SkColor border_color = theme->GetSystemColor(
267 selected() ? ui::NativeTheme::kColorId_FocusedBorderColor 344 selected() ? ui::NativeTheme::kColorId_FocusedBorderColor
268 : ui::NativeTheme::kColorId_UnfocusedBorderColor); 345 : ui::NativeTheme::kColorId_UnfocusedBorderColor);
269 int border_thickness = selected() ? 2 : 1; 346 int border_thickness = selected() ? 2 : 1;
270 SetBorder( 347 if (HasFocus() && selected()) {
271 Border::CreateSolidSidedBorder(0, 0, border_thickness, 0, border_color)); 348 SetBorder(base::MakeUnique<MdTabFocusRingBorder>());
Evan Stade 2016/09/28 21:25:54 Can we just always use MdTabFocusRingBorder? In th
Elly Fong-Jones 2016/09/30 11:54:07 I moved all this logic into MdTabFocusRingBorder::
Evan Stade 2016/09/30 17:24:41 why is it icky? OnPaintBorder wouldn't be virtual
349 } else {
350 SetBorder(Border::CreateSolidSidedBorder(0, 0, border_thickness, 0,
351 border_color));
352 }
272 353
273 SkColor font_color = selected() 354 SkColor font_color = selected()
274 ? theme->GetSystemColor(ui::NativeTheme::kColorId_CallToActionColor) 355 ? theme->GetSystemColor(ui::NativeTheme::kColorId_CallToActionColor)
275 : theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonEnabledColor); 356 : theme->GetSystemColor(ui::NativeTheme::kColorId_ButtonEnabledColor);
276 title()->SetEnabledColor(font_color); 357 title()->SetEnabledColor(font_color);
277 358
278 gfx::Font::Weight font_weight = gfx::Font::Weight::MEDIUM; 359 gfx::Font::Weight font_weight = gfx::Font::Weight::MEDIUM;
279 #if defined(OS_WIN) 360 #if defined(OS_WIN)
280 if (selected()) 361 if (selected())
281 font_weight = gfx::Font::Weight::BOLD; 362 font_weight = gfx::Font::Weight::BOLD;
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
379 OnPaintBackground(canvas); 460 OnPaintBackground(canvas);
380 } 461 }
381 462
382 TabbedPane::TabbedPane() 463 TabbedPane::TabbedPane()
383 : listener_(NULL), 464 : listener_(NULL),
384 tab_strip_(ui::MaterialDesignController::IsSecondaryUiMaterial() 465 tab_strip_(ui::MaterialDesignController::IsSecondaryUiMaterial()
385 ? new MdTabStrip(this) 466 ? new MdTabStrip(this)
386 : new TabStrip(this)), 467 : new TabStrip(this)),
387 contents_(new View()), 468 contents_(new View()),
388 selected_tab_index_(-1) { 469 selected_tab_index_(-1) {
389 #if defined(OS_MACOSX)
390 SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
391 #else
392 SetFocusBehavior(FocusBehavior::ALWAYS);
393 #endif
394
395 AddChildView(tab_strip_); 470 AddChildView(tab_strip_);
396 AddChildView(contents_); 471 AddChildView(contents_);
397 } 472 }
398 473
399 TabbedPane::~TabbedPane() {} 474 TabbedPane::~TabbedPane() {}
400 475
401 int TabbedPane::GetTabCount() { 476 int TabbedPane::GetTabCount() {
402 DCHECK_EQ(tab_strip_->child_count(), contents_->child_count()); 477 DCHECK_EQ(tab_strip_->child_count(), contents_->child_count());
403 return contents_->child_count(); 478 return contents_->child_count();
404 } 479 }
405 480
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) { 481 void TabbedPane::AddTab(const base::string16& title, View* contents) {
412 AddTabAtIndex(tab_strip_->child_count(), title, contents); 482 AddTabAtIndex(tab_strip_->child_count(), title, contents);
413 } 483 }
414 484
415 void TabbedPane::AddTabAtIndex(int index, 485 void TabbedPane::AddTabAtIndex(int index,
416 const base::string16& title, 486 const base::string16& title,
417 View* contents) { 487 View* contents) {
418 DCHECK(index >= 0 && index <= GetTabCount()); 488 DCHECK(index >= 0 && index <= GetTabCount());
419 contents->SetVisible(false); 489 contents->SetVisible(false);
420 490
421 tab_strip_->AddChildViewAt( 491 tab_strip_->AddChildViewAt(
422 ui::MaterialDesignController::IsSecondaryUiMaterial() 492 ui::MaterialDesignController::IsSecondaryUiMaterial()
423 ? new MdTab(this, title, contents) 493 ? new MdTab(this, title, contents)
424 : new Tab(this, title, contents), 494 : new Tab(this, title, contents),
425 index); 495 index);
426 contents_->AddChildViewAt(contents, index); 496 contents_->AddChildViewAt(contents, index);
427 if (selected_tab_index() < 0) 497 if (selected_tab_index() < 0)
428 SelectTabAt(index); 498 SelectTabAt(index);
429 499
430 PreferredSizeChanged(); 500 PreferredSizeChanged();
431 } 501 }
432 502
433 void TabbedPane::SelectTabAt(int index) { 503 void TabbedPane::SelectTabAt(int index) {
434 DCHECK(index >= 0 && index < GetTabCount()); 504 DCHECK(index >= 0 && index < GetTabCount());
435 if (index == selected_tab_index()) 505 if (index == selected_tab_index())
436 return; 506 return;
437 507
438 if (selected_tab_index() >= 0)
439 GetTabAt(selected_tab_index())->SetSelected(false);
440
441 selected_tab_index_ = index;
442 Tab* tab = GetTabAt(index); 508 Tab* tab = GetTabAt(index);
443 tab->SetSelected(true); 509 tab->SetSelected(true);
510 if (GetSelectedTab()) {
511 if (GetSelectedTab()->HasFocus())
512 tab->RequestFocus();
513 GetSelectedTab()->SetSelected(false);
514 }
515 selected_tab_index_ = index;
444 tab_strip_->SchedulePaint(); 516 tab_strip_->SchedulePaint();
445 517
446 FocusManager* focus_manager = tab->contents()->GetFocusManager(); 518 FocusManager* focus_manager = tab->contents()->GetFocusManager();
447 if (focus_manager) { 519 if (focus_manager) {
448 const View* focused_view = focus_manager->GetFocusedView(); 520 const View* focused_view = focus_manager->GetFocusedView();
449 if (focused_view && contents_->Contains(focused_view) && 521 if (focused_view && contents_->Contains(focused_view) &&
450 !tab->contents()->Contains(focused_view)) 522 !tab->contents()->Contains(focused_view))
451 focus_manager->SetFocusedView(tab->contents()); 523 focus_manager->SetFocusedView(tab->contents());
452 } 524 }
453 525
(...skipping 12 matching lines...) Expand all
466 for (int i = 0; i < contents_->child_count(); ++i) 538 for (int i = 0; i < contents_->child_count(); ++i)
467 size.SetToMax(contents_->child_at(i)->GetPreferredSize()); 539 size.SetToMax(contents_->child_at(i)->GetPreferredSize());
468 size.Enlarge(0, tab_strip_->GetPreferredSize().height()); 540 size.Enlarge(0, tab_strip_->GetPreferredSize().height());
469 return size; 541 return size;
470 } 542 }
471 543
472 Tab* TabbedPane::GetTabAt(int index) { 544 Tab* TabbedPane::GetTabAt(int index) {
473 return static_cast<Tab*>(tab_strip_->child_at(index)); 545 return static_cast<Tab*>(tab_strip_->child_at(index));
474 } 546 }
475 547
548 Tab* TabbedPane::GetSelectedTab() {
549 return selected_tab_index() >= 0 ? GetTabAt(selected_tab_index()) : nullptr;
550 }
551
552 bool TabbedPane::MoveSelectionBy(int delta) {
553 const int tab_count = GetTabCount();
554 if (tab_count <= 1)
555 return false;
556 int next_selected_index = (selected_tab_index() + delta) % tab_count;
557 if (next_selected_index < 0)
558 next_selected_index += tab_count;
559 SelectTabAt(next_selected_index);
560 return true;
561 }
562
476 void TabbedPane::Layout() { 563 void TabbedPane::Layout() {
477 const gfx::Size size = tab_strip_->GetPreferredSize(); 564 const gfx::Size size = tab_strip_->GetPreferredSize();
478 tab_strip_->SetBounds(0, 0, width(), size.height()); 565 tab_strip_->SetBounds(0, 0, width(), size.height());
479 contents_->SetBounds(0, tab_strip_->bounds().bottom(), width(), 566 contents_->SetBounds(0, tab_strip_->bounds().bottom(), width(),
480 std::max(0, height() - size.height())); 567 std::max(0, height() - size.height()));
481 for (int i = 0; i < contents_->child_count(); ++i) 568 for (int i = 0; i < contents_->child_count(); ++i)
482 contents_->child_at(i)->SetSize(contents_->size()); 569 contents_->child_at(i)->SetSize(contents_->size());
483 } 570 }
484 571
485 void TabbedPane::ViewHierarchyChanged( 572 void TabbedPane::ViewHierarchyChanged(
486 const ViewHierarchyChangedDetails& details) { 573 const ViewHierarchyChangedDetails& details) {
487 if (details.is_add) { 574 if (details.is_add) {
488 // Support navigating tabs by Ctrl+Tab and Ctrl+Shift+Tab. 575 // Support navigating tabs by Ctrl+Tab and Ctrl+Shift+Tab.
489 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, 576 AddAccelerator(ui::Accelerator(ui::VKEY_TAB,
490 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN)); 577 ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN));
491 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN)); 578 AddAccelerator(ui::Accelerator(ui::VKEY_TAB, ui::EF_CONTROL_DOWN));
492 } 579 }
493 } 580 }
494 581
495 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { 582 bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) {
496 // Handle Ctrl+Tab and Ctrl+Shift+Tab navigation of pages. 583 // Handle Ctrl+Tab and Ctrl+Shift+Tab navigation of pages.
497 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); 584 DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown());
498 const int tab_count = GetTabCount(); 585 return MoveSelectionBy(accelerator.IsShiftDown() ? -1 : 1);
499 if (tab_count <= 1) 586 }
587
588 bool TabbedPane::OnKeyPressed(const ui::KeyEvent& event) {
589 if (!GetSelectedTab() || !GetSelectedTab()->HasFocus())
500 return false; 590 return false;
501 const int increment = accelerator.IsShiftDown() ? -1 : 1; 591 ui::KeyboardCode key = event.key_code();
502 int next_tab_index = (selected_tab_index() + increment) % tab_count; 592 if (key != ui::VKEY_LEFT && key != ui::VKEY_RIGHT)
503 // Wrap around. 593 return false;
504 if (next_tab_index < 0) 594 return MoveSelectionBy(key == ui::VKEY_RIGHT ? 1 : -1);
505 next_tab_index += tab_count;
506 SelectTabAt(next_tab_index);
507 return true;
508 } 595 }
509 596
510 const char* TabbedPane::GetClassName() const { 597 const char* TabbedPane::GetClassName() const {
511 return kViewClassName; 598 return kViewClassName;
512 } 599 }
513 600
514 void TabbedPane::OnFocus() {
515 View::OnFocus();
516
517 View* selected_tab = GetSelectedTab();
518 if (selected_tab) {
519 selected_tab->NotifyAccessibilityEvent(
520 ui::AX_EVENT_FOCUS, true);
521 }
522 }
523
524 void TabbedPane::GetAccessibleState(ui::AXViewState* state) { 601 void TabbedPane::GetAccessibleState(ui::AXViewState* state) {
525 state->role = ui::AX_ROLE_TAB_LIST; 602 state->role = ui::AX_ROLE_TAB_LIST;
526 } 603 }
527 604
528 } // namespace views 605 } // 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