Chromium Code Reviews| Index: ui/views/controls/tabbed_pane/tabbed_pane.cc |
| diff --git a/ui/views/controls/tabbed_pane/tabbed_pane.cc b/ui/views/controls/tabbed_pane/tabbed_pane.cc |
| index b445db30b5cef9f364aaeb3f6be7651c51800c9f..ea1462f03d5fedd3ea01a979f6bcb1f38e888edf 100644 |
| --- a/ui/views/controls/tabbed_pane/tabbed_pane.cc |
| +++ b/ui/views/controls/tabbed_pane/tabbed_pane.cc |
| @@ -60,6 +60,11 @@ class Tab : public View { |
| bool selected() const { return contents_->visible(); } |
| void SetSelected(bool selected); |
| + // 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
|
| + // containing TabStrip is, Tabs need to be told when the TabStrip gains or |
| + // loses focus so they can swap to/from the focus ring border. |
| + void OnContainerFocusChanged(); |
| + |
| // Overridden from View: |
| bool OnMousePressed(const ui::MouseEvent& event) override; |
| void OnMouseEntered(const ui::MouseEvent& event) override; |
| @@ -75,6 +80,9 @@ class Tab : public View { |
| // Called whenever |tab_state_| changes. |
| virtual void OnStateChanged(); |
| + // Returns whether the containing TabStrip has focus. |
| + bool ContainerHasFocus(); |
| + |
| private: |
| enum TabState { |
| TAB_INACTIVE, |
| @@ -252,6 +260,65 @@ void Tab::SetState(TabState tab_state) { |
| SchedulePaint(); |
| } |
| +bool Tab::ContainerHasFocus() { |
| + return tabbed_pane_->HasFocus(); |
| +} |
| + |
| +void Tab::OnContainerFocusChanged() { |
| + OnStateChanged(); |
| + SchedulePaint(); |
| +} |
| + |
| +// The border used for MdTabs when they are both focused and selected: a |
| +// rectangle with the bottom side in a base color and the other sides in a |
| +// lightened version of the base color. |
| +class MdTabFocusRingBorder : public Border { |
| + public: |
| + 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
|
| + ~MdTabFocusRingBorder() override; |
| + |
| + private: |
| + void Paint(const View& view, gfx::Canvas* canvas) override; |
| + gfx::Insets GetInsets() const override; |
| + gfx::Size GetMinimumSize() const override; |
| + |
| + SkColor base_color_; |
| +}; |
| + |
| +MdTabFocusRingBorder::MdTabFocusRingBorder(SkColor base_color) |
| + : base_color_(base_color) {} |
| +MdTabFocusRingBorder::~MdTabFocusRingBorder() {} |
| + |
| +void MdTabFocusRingBorder::Paint(const View& view, gfx::Canvas* canvas) { |
| + gfx::Insets insets = GetInsets(); |
| + // TODO(ellyjones): should this 0x66 be part of NativeTheme somehow? |
| + SkColor light_color = SkColorSetA(base_color_, 0x66); |
| + |
| + SkPaint paint; |
| + paint.setColor(light_color); |
| + paint.setStyle(SkPaint::kStroke_Style); |
| + paint.setStrokeWidth(2); |
| + |
| + gfx::Rect bounds = gfx::Rect(0, 0, view.width(), view.height()); |
| + 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.
|
| + |
| + // Draw the lighter-colored stroke first, then draw the heavier stroke over |
| + // the bottom of it. This is fine because the heavier stroke has 1.0 alpha, so |
| + // the lighter stroke won't show through. |
| + canvas->DrawRect(bounds, paint); |
| + canvas->FillRect(gfx::Rect(0, view.height() - insets.bottom(), view.width(), |
| + insets.bottom()), |
| + base_color_); |
| +} |
| + |
| +gfx::Insets MdTabFocusRingBorder::GetInsets() const { |
| + return gfx::Insets(2, 2); |
| +} |
| + |
| +gfx::Size MdTabFocusRingBorder::GetMinimumSize() const { |
| + 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
|
| +} |
| + |
| MdTab::MdTab(TabbedPane* tabbed_pane, |
| const base::string16& title, |
| View* contents) |
| @@ -267,8 +334,14 @@ void MdTab::OnStateChanged() { |
| selected() ? ui::NativeTheme::kColorId_FocusedBorderColor |
| : ui::NativeTheme::kColorId_UnfocusedBorderColor); |
| 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
|
| - SetBorder( |
| - Border::CreateSolidSidedBorder(0, 0, border_thickness, 0, border_color)); |
| + if (ContainerHasFocus() && selected()) { |
| + SetBorder( |
| + base::MakeUnique<MdTabFocusRingBorder>(GetNativeTheme()->GetSystemColor( |
| + ui::NativeTheme::kColorId_FocusedBorderColor))); |
| + } else { |
| + SetBorder(Border::CreateSolidSidedBorder(0, 0, border_thickness, 0, |
| + border_color)); |
| + } |
| SkColor font_color = selected() |
| ? theme->GetSystemColor(ui::NativeTheme::kColorId_CallToActionColor) |
| @@ -403,11 +476,6 @@ int TabbedPane::GetTabCount() { |
| return contents_->child_count(); |
| } |
| -View* TabbedPane::GetSelectedTab() { |
| - return selected_tab_index() < 0 ? |
| - NULL : GetTabAt(selected_tab_index())->contents(); |
| -} |
| - |
| void TabbedPane::AddTab(const base::string16& title, View* contents) { |
| AddTabAtIndex(tab_strip_->child_count(), title, contents); |
| } |
| @@ -435,8 +503,8 @@ void TabbedPane::SelectTabAt(int index) { |
| if (index == selected_tab_index()) |
| return; |
| - if (selected_tab_index() >= 0) |
| - GetTabAt(selected_tab_index())->SetSelected(false); |
| + if (GetSelectedTab()) |
| + GetSelectedTab()->SetSelected(false); |
| selected_tab_index_ = index; |
| Tab* tab = GetTabAt(index); |
| @@ -473,6 +541,21 @@ Tab* TabbedPane::GetTabAt(int index) { |
| return static_cast<Tab*>(tab_strip_->child_at(index)); |
| } |
| +Tab* TabbedPane::GetSelectedTab() { |
| + return selected_tab_index() >= 0 ? GetTabAt(selected_tab_index()) : nullptr; |
| +} |
| + |
| +bool TabbedPane::MoveSelectionBy(int delta) { |
| + const int tab_count = GetTabCount(); |
| + if (tab_count <= 1) |
| + return false; |
| + int next_selected_index = (selected_tab_index() + delta) % tab_count; |
| + if (next_selected_index < 0) |
| + next_selected_index += tab_count; |
| + SelectTabAt(next_selected_index); |
| + return true; |
| +} |
| + |
| void TabbedPane::Layout() { |
| const gfx::Size size = tab_strip_->GetPreferredSize(); |
| tab_strip_->SetBounds(0, 0, width(), size.height()); |
| @@ -495,16 +578,16 @@ void TabbedPane::ViewHierarchyChanged( |
| bool TabbedPane::AcceleratorPressed(const ui::Accelerator& accelerator) { |
| // Handle Ctrl+Tab and Ctrl+Shift+Tab navigation of pages. |
| DCHECK(accelerator.key_code() == ui::VKEY_TAB && accelerator.IsCtrlDown()); |
| - const int tab_count = GetTabCount(); |
| - if (tab_count <= 1) |
| + return MoveSelectionBy(accelerator.IsShiftDown() ? -1 : 1); |
| +} |
| + |
| +bool TabbedPane::OnKeyPressed(const ui::KeyEvent& event) { |
| + if (!HasFocus()) |
| return false; |
| - const int increment = accelerator.IsShiftDown() ? -1 : 1; |
| - int next_tab_index = (selected_tab_index() + increment) % tab_count; |
| - // Wrap around. |
| - if (next_tab_index < 0) |
| - next_tab_index += tab_count; |
| - SelectTabAt(next_tab_index); |
| - return true; |
| + ui::DomKey key = event.GetDomKey(); |
| + if (key != ui::DomKey::ARROW_LEFT && key != ui::DomKey::ARROW_RIGHT) |
| + return false; |
| + return MoveSelectionBy(key == ui::DomKey::ARROW_LEFT ? 1 : -1); |
| } |
| const char* TabbedPane::GetClassName() const { |
| @@ -514,13 +597,21 @@ const char* TabbedPane::GetClassName() const { |
| void TabbedPane::OnFocus() { |
| View::OnFocus(); |
| - View* selected_tab = GetSelectedTab(); |
| - if (selected_tab) { |
| - selected_tab->NotifyAccessibilityEvent( |
| - ui::AX_EVENT_FOCUS, true); |
| + Tab* selected = GetSelectedTab(); |
| + if (selected) { |
| + selected->OnContainerFocusChanged(); |
| + if (selected->contents()) |
| + selected->contents()->NotifyAccessibilityEvent(ui::AX_EVENT_FOCUS, true); |
| } |
| } |
| +void TabbedPane::OnBlur() { |
| + View::OnBlur(); |
| + Tab* selected_tab = GetSelectedTab(); |
| + if (selected_tab) |
| + selected_tab->OnContainerFocusChanged(); |
| +} |
| + |
| void TabbedPane::GetAccessibleState(ui::AXViewState* state) { |
| state->role = ui::AX_ROLE_TAB_LIST; |
| } |