| 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 <memory> | 7 #include <memory> |
| 8 | 8 |
| 9 #include "base/macros.h" | 9 #include "base/macros.h" |
| 10 #include "base/message_loop/message_loop.h" | 10 #include "base/message_loop/message_loop.h" |
| 11 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 #include "ui/accessibility/ax_action_data.h" |
| 14 #include "ui/accessibility/ax_enums.h" |
| 13 #include "ui/events/keycodes/keyboard_code_conversion.h" | 15 #include "ui/events/keycodes/keyboard_code_conversion.h" |
| 16 #include "ui/views/accessibility/native_view_accessibility.h" |
| 17 #include "ui/views/test/test_views.h" |
| 14 #include "ui/views/test/views_test_base.h" | 18 #include "ui/views/test/views_test_base.h" |
| 19 #include "ui/views/widget/widget.h" |
| 15 | 20 |
| 16 using base::ASCIIToUTF16; | 21 using base::ASCIIToUTF16; |
| 17 | 22 |
| 18 namespace views { | 23 namespace views { |
| 24 namespace test { |
| 25 namespace { |
| 19 | 26 |
| 20 // A view for testing that takes a fixed preferred size upon construction. | 27 base::string16 DefaultTabTitle() { |
| 21 class FixedSizeView : public View { | 28 return ASCIIToUTF16("tab"); |
| 29 } |
| 30 |
| 31 } // namespace |
| 32 |
| 33 class TabbedPaneTest : public ViewsTestBase { |
| 22 public: | 34 public: |
| 23 explicit FixedSizeView(const gfx::Size& size) | 35 TabbedPaneTest() { |
| 24 : size_(size) {} | 36 tabbed_pane_ = base::MakeUnique<TabbedPane>(); |
| 37 tabbed_pane_->set_owned_by_client(); |
| 38 } |
| 25 | 39 |
| 26 // Overridden from View: | 40 protected: |
| 27 gfx::Size GetPreferredSize() const override { return size_; } | 41 Tab* GetTabAt(int index) { |
| 42 return static_cast<Tab*>(tabbed_pane_->tab_strip_->child_at(index)); |
| 43 } |
| 44 |
| 45 View* GetSelectedTabContentView() { |
| 46 return tabbed_pane_->GetSelectedTabContentView(); |
| 47 } |
| 48 |
| 49 void SendKeyPressToSelectedTab(ui::KeyboardCode keyboard_code) { |
| 50 tabbed_pane_->GetSelectedTab()->OnKeyPressed( |
| 51 ui::KeyEvent(ui::ET_KEY_PRESSED, keyboard_code, |
| 52 ui::UsLayoutKeyboardCodeToDomCode(keyboard_code), 0)); |
| 53 } |
| 54 |
| 55 std::unique_ptr<TabbedPane> tabbed_pane_; |
| 28 | 56 |
| 29 private: | 57 private: |
| 30 const gfx::Size size_; | 58 DISALLOW_COPY_AND_ASSIGN(TabbedPaneTest); |
| 31 | |
| 32 DISALLOW_COPY_AND_ASSIGN(FixedSizeView); | |
| 33 }; | 59 }; |
| 34 | 60 |
| 35 typedef ViewsTestBase TabbedPaneTest; | |
| 36 | |
| 37 // Tests TabbedPane::GetPreferredSize() and TabbedPane::Layout(). | 61 // Tests TabbedPane::GetPreferredSize() and TabbedPane::Layout(). |
| 38 TEST_F(TabbedPaneTest, SizeAndLayout) { | 62 TEST_F(TabbedPaneTest, SizeAndLayout) { |
| 39 std::unique_ptr<TabbedPane> tabbed_pane(new TabbedPane()); | 63 View* child1 = new StaticSizedView(gfx::Size(20, 10)); |
| 40 View* child1 = new FixedSizeView(gfx::Size(20, 10)); | 64 tabbed_pane_->AddTab(ASCIIToUTF16("tab1"), child1); |
| 41 tabbed_pane->AddTab(ASCIIToUTF16("tab1"), child1); | 65 View* child2 = new StaticSizedView(gfx::Size(5, 5)); |
| 42 View* child2 = new FixedSizeView(gfx::Size(5, 5)); | 66 tabbed_pane_->AddTab(ASCIIToUTF16("tab2"), child2); |
| 43 tabbed_pane->AddTab(ASCIIToUTF16("tab2"), child2); | 67 tabbed_pane_->SelectTabAt(0); |
| 44 tabbed_pane->SelectTabAt(0); | |
| 45 | 68 |
| 46 // The |tabbed_pane| implementation of Views has no border by default. | 69 // The |tabbed_pane_| implementation of Views has no border by default. |
| 47 // Therefore it should be as wide as the widest tab. The native Windows | 70 // Therefore it should be as wide as the widest tab. The native Windows |
| 48 // tabbed pane has a border that used up extra space. Therefore the preferred | 71 // tabbed pane has a border that used up extra space. Therefore the preferred |
| 49 // width is larger than the largest child. | 72 // width is larger than the largest child. |
| 50 gfx::Size pref(tabbed_pane->GetPreferredSize()); | 73 gfx::Size pref(tabbed_pane_->GetPreferredSize()); |
| 51 EXPECT_GE(pref.width(), 20); | 74 EXPECT_GE(pref.width(), 20); |
| 52 EXPECT_GT(pref.height(), 10); | 75 EXPECT_GT(pref.height(), 10); |
| 53 | 76 |
| 54 // The bounds of our children should be smaller than the tabbed pane's bounds. | 77 // The bounds of our children should be smaller than the tabbed pane's bounds. |
| 55 tabbed_pane->SetBounds(0, 0, 100, 200); | 78 tabbed_pane_->SetBounds(0, 0, 100, 200); |
| 56 RunPendingMessages(); | 79 RunPendingMessages(); |
| 57 gfx::Rect bounds(child1->bounds()); | 80 gfx::Rect bounds(child1->bounds()); |
| 58 EXPECT_GT(bounds.width(), 0); | 81 EXPECT_GT(bounds.width(), 0); |
| 59 // The |tabbed_pane| has no border. Therefore the children should be as wide | 82 // The |tabbed_pane_| has no border. Therefore the children should be as wide |
| 60 // as the |tabbed_pane|. | 83 // as the |tabbed_pane_|. |
| 61 EXPECT_LE(bounds.width(), 100); | 84 EXPECT_LE(bounds.width(), 100); |
| 62 EXPECT_GT(bounds.height(), 0); | 85 EXPECT_GT(bounds.height(), 0); |
| 63 EXPECT_LT(bounds.height(), 200); | 86 EXPECT_LT(bounds.height(), 200); |
| 64 | 87 |
| 65 // If we switch to the other tab, it should get assigned the same bounds. | 88 // If we switch to the other tab, it should get assigned the same bounds. |
| 66 tabbed_pane->SelectTabAt(1); | 89 tabbed_pane_->SelectTabAt(1); |
| 67 EXPECT_EQ(bounds, child2->bounds()); | 90 EXPECT_EQ(bounds, child2->bounds()); |
| 68 } | 91 } |
| 69 | 92 |
| 70 TEST_F(TabbedPaneTest, AddAndSelect) { | 93 TEST_F(TabbedPaneTest, AddAndSelect) { |
| 71 std::unique_ptr<TabbedPane> tabbed_pane(new TabbedPane()); | 94 // Add several tabs; only the first should be selected automatically. |
| 72 // Add several tabs; only the first should be a selected automatically. | |
| 73 for (int i = 0; i < 3; ++i) { | 95 for (int i = 0; i < 3; ++i) { |
| 74 View* tab = new View(); | 96 View* tab = new View(); |
| 75 tabbed_pane->AddTab(ASCIIToUTF16("tab"), tab); | 97 tabbed_pane_->AddTab(DefaultTabTitle(), tab); |
| 76 EXPECT_EQ(i + 1, tabbed_pane->GetTabCount()); | 98 EXPECT_EQ(i + 1, tabbed_pane_->GetTabCount()); |
| 77 EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex()); | 99 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 78 } | 100 } |
| 79 | 101 |
| 80 // Select each tab. | 102 // Select each tab. |
| 81 for (int i = 0; i < tabbed_pane->GetTabCount(); ++i) { | 103 for (int i = 0; i < tabbed_pane_->GetTabCount(); ++i) { |
| 82 tabbed_pane->SelectTabAt(i); | 104 tabbed_pane_->SelectTabAt(i); |
| 83 EXPECT_EQ(i, tabbed_pane->GetSelectedTabIndex()); | 105 EXPECT_EQ(i, tabbed_pane_->GetSelectedTabIndex()); |
| 84 } | 106 } |
| 85 | 107 |
| 86 // Add a tab at index 0, it should not be selected automatically. | 108 // Add a tab at index 0, it should not be selected automatically. |
| 87 View* tab0 = new View(); | 109 View* tab0 = new View(); |
| 88 tabbed_pane->AddTabAtIndex(0, ASCIIToUTF16("tab0"), tab0); | 110 tabbed_pane_->AddTabAtIndex(0, ASCIIToUTF16("tab0"), tab0); |
| 89 EXPECT_NE(tab0, tabbed_pane->GetSelectedTabContentView()); | 111 EXPECT_NE(tab0, GetSelectedTabContentView()); |
| 90 EXPECT_NE(0, tabbed_pane->GetSelectedTabIndex()); | 112 EXPECT_NE(0, tabbed_pane_->GetSelectedTabIndex()); |
| 91 } | |
| 92 | |
| 93 ui::KeyEvent MakeKeyPressedEvent(ui::KeyboardCode keyboard_code, int flags) { | |
| 94 return ui::KeyEvent(ui::ET_KEY_PRESSED, keyboard_code, | |
| 95 ui::UsLayoutKeyboardCodeToDomCode(keyboard_code), flags); | |
| 96 } | 113 } |
| 97 | 114 |
| 98 TEST_F(TabbedPaneTest, ArrowKeyBindings) { | 115 TEST_F(TabbedPaneTest, ArrowKeyBindings) { |
| 99 std::unique_ptr<TabbedPane> tabbed_pane(new TabbedPane()); | 116 // Add several tabs; only the first should be selected automatically. |
| 100 // Add several tabs; only the first should be a selected automatically. | |
| 101 for (int i = 0; i < 3; ++i) { | 117 for (int i = 0; i < 3; ++i) { |
| 102 View* tab = new View(); | 118 View* tab = new View(); |
| 103 tabbed_pane->AddTab(ASCIIToUTF16("tab"), tab); | 119 tabbed_pane_->AddTab(DefaultTabTitle(), tab); |
| 104 EXPECT_EQ(i + 1, tabbed_pane->GetTabCount()); | 120 EXPECT_EQ(i + 1, tabbed_pane_->GetTabCount()); |
| 105 } | 121 } |
| 106 | 122 |
| 107 EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex()); | 123 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 108 | 124 |
| 109 // Right arrow should select tab 1: | 125 // Right arrow should select tab 1: |
| 110 tabbed_pane->GetSelectedTab()->OnKeyPressed( | 126 SendKeyPressToSelectedTab(ui::VKEY_RIGHT); |
| 111 MakeKeyPressedEvent(ui::VKEY_RIGHT, 0)); | 127 EXPECT_EQ(1, tabbed_pane_->GetSelectedTabIndex()); |
| 112 EXPECT_EQ(1, tabbed_pane->GetSelectedTabIndex()); | |
| 113 | 128 |
| 114 // Left arrow should select tab 0: | 129 // Left arrow should select tab 0: |
| 115 tabbed_pane->GetSelectedTab()->OnKeyPressed( | 130 SendKeyPressToSelectedTab(ui::VKEY_LEFT); |
| 116 MakeKeyPressedEvent(ui::VKEY_LEFT, 0)); | 131 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 117 EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex()); | |
| 118 | 132 |
| 119 // Left arrow again should wrap to tab 2: | 133 // Left arrow again should wrap to tab 2: |
| 120 tabbed_pane->GetSelectedTab()->OnKeyPressed( | 134 SendKeyPressToSelectedTab(ui::VKEY_LEFT); |
| 121 MakeKeyPressedEvent(ui::VKEY_LEFT, 0)); | 135 EXPECT_EQ(2, tabbed_pane_->GetSelectedTabIndex()); |
| 122 EXPECT_EQ(2, tabbed_pane->GetSelectedTabIndex()); | |
| 123 | 136 |
| 124 // Right arrow again should wrap to tab 0: | 137 // Right arrow again should wrap to tab 0: |
| 125 tabbed_pane->GetSelectedTab()->OnKeyPressed( | 138 SendKeyPressToSelectedTab(ui::VKEY_RIGHT); |
| 126 MakeKeyPressedEvent(ui::VKEY_RIGHT, 0)); | 139 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 127 EXPECT_EQ(0, tabbed_pane->GetSelectedTabIndex()); | |
| 128 } | 140 } |
| 129 | 141 |
| 142 // Use TabbedPane::HandleAccessibleAction() to select tabs and make sure their |
| 143 // a11y information is correct. |
| 144 TEST_F(TabbedPaneTest, SelectTabWithAccessibleAction) { |
| 145 // Testing accessibility information requires the View to have a Widget. |
| 146 Widget* widget = new Widget; |
| 147 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); |
| 148 widget->Init(params); |
| 149 widget->GetContentsView()->AddChildView(tabbed_pane_.get()); |
| 150 widget->Show(); |
| 151 |
| 152 constexpr int kNumTabs = 3; |
| 153 for (int i = 0; i < kNumTabs; ++i) { |
| 154 tabbed_pane_->AddTab(DefaultTabTitle(), new View()); |
| 155 } |
| 156 // Check the first tab is selected. |
| 157 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 158 |
| 159 // Check the a11y information for each tab. |
| 160 for (int i = 0; i < kNumTabs; ++i) { |
| 161 NativeViewAccessibility* nva = NativeViewAccessibility::Create(GetTabAt(i)); |
| 162 ui::AXNodeData data = nva->GetData(); |
| 163 SCOPED_TRACE(testing::Message() << "Tab at index: " << i); |
| 164 EXPECT_EQ(ui::AX_ROLE_TAB, data.role); |
| 165 EXPECT_EQ(DefaultTabTitle(), data.GetString16Attribute(ui::AX_ATTR_NAME)); |
| 166 EXPECT_TRUE(data.HasStateFlag(ui::AX_STATE_SELECTABLE)); |
| 167 EXPECT_EQ(i == 0, data.HasStateFlag(ui::AX_STATE_SELECTED)); |
| 168 nva->Destroy(); |
| 169 } |
| 170 |
| 171 ui::AXActionData action; |
| 172 action.action = ui::AX_ACTION_SET_SELECTION; |
| 173 // Select the first tab. |
| 174 NativeViewAccessibility* nva = NativeViewAccessibility::Create(GetTabAt(0)); |
| 175 nva->AccessibilityPerformAction(action); |
| 176 EXPECT_EQ(0, tabbed_pane_->GetSelectedTabIndex()); |
| 177 nva->Destroy(); |
| 178 |
| 179 // Select the second tab. |
| 180 nva = NativeViewAccessibility::Create(GetTabAt(1)); |
| 181 nva->AccessibilityPerformAction(action); |
| 182 EXPECT_EQ(1, tabbed_pane_->GetSelectedTabIndex()); |
| 183 // Select the second tab again. |
| 184 nva->AccessibilityPerformAction(action); |
| 185 EXPECT_EQ(1, tabbed_pane_->GetSelectedTabIndex()); |
| 186 nva->Destroy(); |
| 187 |
| 188 widget->CloseNow(); |
| 189 } |
| 190 |
| 191 } // namespace test |
| 130 } // namespace views | 192 } // namespace views |
| OLD | NEW |