Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |