| 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/table/table_view.h" | 5 #include "ui/views/controls/table/table_view.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include "base/macros.h" | 9 #include "base/macros.h" |
| 10 #include "base/run_loop.h" |
| 10 #include "base/strings/string_number_conversions.h" | 11 #include "base/strings/string_number_conversions.h" |
| 11 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" | 13 #include "testing/gtest/include/gtest/gtest.h" |
| 13 #include "ui/events/event_utils.h" | 14 #include "ui/events/event_utils.h" |
| 15 #include "ui/events/test/event_generator.h" |
| 14 #include "ui/views/controls/table/table_grouper.h" | 16 #include "ui/views/controls/table/table_grouper.h" |
| 15 #include "ui/views/controls/table/table_header.h" | 17 #include "ui/views/controls/table/table_header.h" |
| 16 #include "ui/views/controls/table/table_view_observer.h" | 18 #include "ui/views/controls/table/table_view_observer.h" |
| 19 #include "ui/views/test/views_test_base.h" |
| 17 | 20 |
| 18 // Put the tests in the views namespace to make it easier to declare them as | 21 // Put the tests in the views namespace to make it easier to declare them as |
| 19 // friend classes. | 22 // friend classes. |
| 20 namespace views { | 23 namespace views { |
| 21 | 24 |
| 22 class TableViewTestHelper { | 25 class TableViewTestHelper { |
| 23 public: | 26 public: |
| 24 explicit TableViewTestHelper(TableView* table) : table_(table) {} | 27 explicit TableViewTestHelper(TableView* table) : table_(table) {} |
| 25 | 28 |
| 26 std::string GetPaintRegion(const gfx::Rect& bounds) { | 29 std::string GetPaintRegion(const gfx::Rect& bounds) { |
| (...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 // Overriden so key processing works. | 199 // Overriden so key processing works. |
| 197 return true; | 200 return true; |
| 198 } | 201 } |
| 199 | 202 |
| 200 private: | 203 private: |
| 201 DISALLOW_COPY_AND_ASSIGN(TestTableView); | 204 DISALLOW_COPY_AND_ASSIGN(TestTableView); |
| 202 }; | 205 }; |
| 203 | 206 |
| 204 } // namespace | 207 } // namespace |
| 205 | 208 |
| 206 class TableViewTest : public testing::Test { | 209 class TableViewTest : public ViewsTestBase { |
| 207 public: | 210 public: |
| 208 TableViewTest() : table_(NULL) {} | 211 TableViewTest() : table_(NULL) {} |
| 209 | 212 |
| 210 void SetUp() override { | 213 void SetUp() override { |
| 214 ViewsTestBase::SetUp(); |
| 215 |
| 211 model_.reset(new TestTableModel2); | 216 model_.reset(new TestTableModel2); |
| 212 std::vector<ui::TableColumn> columns(2); | 217 std::vector<ui::TableColumn> columns(2); |
| 213 columns[0].title = base::ASCIIToUTF16("Title Column 0"); | 218 columns[0].title = base::ASCIIToUTF16("Title Column 0"); |
| 214 columns[0].sortable = true; | 219 columns[0].sortable = true; |
| 215 columns[1].title = base::ASCIIToUTF16("Title Column 1"); | 220 columns[1].title = base::ASCIIToUTF16("Title Column 1"); |
| 216 columns[1].id = 1; | 221 columns[1].id = 1; |
| 217 columns[1].sortable = true; | 222 columns[1].sortable = true; |
| 218 table_ = new TestTableView(model_.get(), columns); | 223 table_ = new TestTableView(model_.get(), columns); |
| 219 parent_.reset(table_->CreateParentIfNecessary()); | 224 View* parent = table_->CreateParentIfNecessary(); |
| 220 parent_->SetBounds(0, 0, 10000, 10000); | 225 parent->SetBounds(0, 0, 10000, 10000); |
| 221 parent_->Layout(); | 226 parent->Layout(); |
| 222 helper_.reset(new TableViewTestHelper(table_)); | 227 helper_.reset(new TableViewTestHelper(table_)); |
| 228 |
| 229 Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW); |
| 230 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; |
| 231 params.bounds = gfx::Rect(0, 0, 650, 650); |
| 232 widget_.reset(new Widget); |
| 233 widget_->Init(params); |
| 234 widget_->GetContentsView()->AddChildView(parent); |
| 235 widget_->Show(); |
| 236 } |
| 237 |
| 238 void TearDown() override { |
| 239 widget_.reset(); |
| 240 ViewsTestBase::TearDown(); |
| 223 } | 241 } |
| 224 | 242 |
| 225 void ClickOnRow(int row, int flags) { | 243 void ClickOnRow(int row, int flags) { |
| 226 const int y = row * table_->row_height(); | 244 ui::test::EventGenerator generator(widget_->GetNativeWindow()); |
| 227 const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y), | 245 generator.set_flags(flags); |
| 228 gfx::Point(0, y), ui::EventTimeForNow(), | 246 generator.set_current_location(GetPointForRow(row)); |
| 229 ui::EF_LEFT_MOUSE_BUTTON | flags, | 247 generator.PressLeftButton(); |
| 230 ui::EF_LEFT_MOUSE_BUTTON); | |
| 231 table_->OnMousePressed(pressed); | |
| 232 } | 248 } |
| 233 | 249 |
| 234 void TapOnRow(int row) { | 250 void TapOnRow(int row) { |
| 235 const int y = row * table_->row_height(); | 251 ui::test::EventGenerator generator(widget_->GetNativeWindow()); |
| 236 const ui::GestureEventDetails event_details(ui::ET_GESTURE_TAP); | 252 generator.GestureTapAt(GetPointForRow(row)); |
| 237 ui::GestureEvent tap(0, y, 0, base::TimeTicks(), event_details); | |
| 238 table_->OnGestureEvent(&tap); | |
| 239 } | 253 } |
| 240 | 254 |
| 241 // Returns the state of the selection model as a string. The format is: | 255 // Returns the state of the selection model as a string. The format is: |
| 242 // 'active=X anchor=X selection=X X X...'. | 256 // 'active=X anchor=X selection=X X X...'. |
| 243 std::string SelectionStateAsString() const { | 257 std::string SelectionStateAsString() const { |
| 244 const ui::ListSelectionModel& model(table_->selection_model()); | 258 const ui::ListSelectionModel& model(table_->selection_model()); |
| 245 std::string result = "active=" + base::IntToString(model.active()) + | 259 std::string result = "active=" + base::IntToString(model.active()) + |
| 246 " anchor=" + base::IntToString(model.anchor()) + | 260 " anchor=" + base::IntToString(model.anchor()) + |
| 247 " selection="; | 261 " selection="; |
| 248 const ui::ListSelectionModel::SelectedIndices& selection( | 262 const ui::ListSelectionModel::SelectedIndices& selection( |
| 249 model.selected_indices()); | 263 model.selected_indices()); |
| 250 for (size_t i = 0; i < selection.size(); ++i) { | 264 for (size_t i = 0; i < selection.size(); ++i) { |
| 251 if (i != 0) | 265 if (i != 0) |
| 252 result += " "; | 266 result += " "; |
| 253 result += base::IntToString(selection[i]); | 267 result += base::IntToString(selection[i]); |
| 254 } | 268 } |
| 255 return result; | 269 return result; |
| 256 } | 270 } |
| 257 | 271 |
| 258 void PressKey(ui::KeyboardCode code) { | 272 void PressKey(ui::KeyboardCode code) { |
| 259 ui::KeyEvent event(ui::ET_KEY_PRESSED, code, ui::EF_NONE); | 273 ui::test::EventGenerator generator(widget_->GetNativeWindow()); |
| 260 table_->OnKeyPressed(event); | 274 generator.PressKey(code, ui::EF_NONE); |
| 261 } | 275 } |
| 262 | 276 |
| 263 protected: | 277 protected: |
| 264 std::unique_ptr<TestTableModel2> model_; | 278 std::unique_ptr<TestTableModel2> model_; |
| 265 | 279 |
| 266 // Owned by |parent_|. | 280 // Owned by |parent_|. |
| 267 TableView* table_; | 281 TableView* table_; |
| 268 | 282 |
| 269 std::unique_ptr<TableViewTestHelper> helper_; | 283 std::unique_ptr<TableViewTestHelper> helper_; |
| 270 | 284 |
| 271 private: | 285 private: |
| 272 std::unique_ptr<View> parent_; | 286 gfx::Point GetPointForRow(int row) { |
| 287 const int y = row * table_->row_height(); |
| 288 return table_->GetBoundsInScreen().origin() + gfx::Vector2d(5, y); |
| 289 } |
| 290 |
| 291 std::unique_ptr<Widget> widget_; |
| 273 | 292 |
| 274 DISALLOW_COPY_AND_ASSIGN(TableViewTest); | 293 DISALLOW_COPY_AND_ASSIGN(TableViewTest); |
| 275 }; | 294 }; |
| 276 | 295 |
| 277 // Verifies GetPaintRegion. | 296 // Verifies GetPaintRegion. |
| 278 TEST_F(TableViewTest, GetPaintRegion) { | 297 TEST_F(TableViewTest, GetPaintRegion) { |
| 279 // Two columns should be visible. | 298 // Two columns should be visible. |
| 280 EXPECT_EQ(2u, helper_->visible_col_count()); | 299 EXPECT_EQ(2u, helper_->visible_col_count()); |
| 281 | 300 |
| 282 EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds())); | 301 EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds())); |
| (...skipping 476 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 759 ranges.push_back(2); | 778 ranges.push_back(2); |
| 760 grouper.SetRanges(ranges); | 779 grouper.SetRanges(ranges); |
| 761 table_->SetGrouper(&grouper); | 780 table_->SetGrouper(&grouper); |
| 762 | 781 |
| 763 TableViewObserverImpl observer; | 782 TableViewObserverImpl observer; |
| 764 table_->set_observer(&observer); | 783 table_->set_observer(&observer); |
| 765 | 784 |
| 766 // Initially no selection. | 785 // Initially no selection. |
| 767 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); | 786 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); |
| 768 | 787 |
| 769 PressKey(ui::VKEY_DOWN); | 788 // Focus selects the first row. |
| 789 table_->RequestFocus(); |
| 790 EXPECT_EQ(0, observer.GetChangedCountAndClear()); |
| 791 // ... after a delay. |
| 792 base::RunLoop().RunUntilIdle(); |
| 770 EXPECT_EQ(1, observer.GetChangedCountAndClear()); | 793 EXPECT_EQ(1, observer.GetChangedCountAndClear()); |
| 771 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); | 794 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); |
| 772 | 795 |
| 773 PressKey(ui::VKEY_DOWN); | 796 PressKey(ui::VKEY_DOWN); |
| 774 EXPECT_EQ(1, observer.GetChangedCountAndClear()); | 797 EXPECT_EQ(1, observer.GetChangedCountAndClear()); |
| 775 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString()); | 798 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString()); |
| 776 | 799 |
| 777 PressKey(ui::VKEY_DOWN); | 800 PressKey(ui::VKEY_DOWN); |
| 778 EXPECT_EQ(1, observer.GetChangedCountAndClear()); | 801 EXPECT_EQ(1, observer.GetChangedCountAndClear()); |
| 779 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString()); | 802 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString()); |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 886 std::vector<int> ranges; | 909 std::vector<int> ranges; |
| 887 ranges.push_back(2); | 910 ranges.push_back(2); |
| 888 ranges.push_back(1); | 911 ranges.push_back(1); |
| 889 ranges.push_back(2); | 912 ranges.push_back(2); |
| 890 grouper.SetRanges(ranges); | 913 grouper.SetRanges(ranges); |
| 891 table_->SetGrouper(&grouper); | 914 table_->SetGrouper(&grouper); |
| 892 | 915 |
| 893 TableViewObserverImpl observer; | 916 TableViewObserverImpl observer; |
| 894 table_->set_observer(&observer); | 917 table_->set_observer(&observer); |
| 895 | 918 |
| 896 // Initially no selection. | 919 // Focus selects the first row. |
| 897 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); | 920 table_->RequestFocus(); |
| 921 EXPECT_EQ(0, observer.GetChangedCountAndClear()); |
| 922 // ... after a delay. |
| 923 base::RunLoop().RunUntilIdle(); |
| 924 EXPECT_EQ(1, observer.GetChangedCountAndClear()); |
| 925 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); |
| 898 | 926 |
| 899 PressKey(ui::VKEY_HOME); | 927 PressKey(ui::VKEY_HOME); |
| 900 EXPECT_EQ(1, observer.GetChangedCountAndClear()); | 928 EXPECT_EQ(0, observer.GetChangedCountAndClear()); |
| 901 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); | 929 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); |
| 902 | 930 |
| 903 PressKey(ui::VKEY_END); | 931 PressKey(ui::VKEY_END); |
| 904 EXPECT_EQ(1, observer.GetChangedCountAndClear()); | 932 EXPECT_EQ(1, observer.GetChangedCountAndClear()); |
| 905 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); | 933 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); |
| 906 | 934 |
| 935 PressKey(ui::VKEY_HOME); |
| 936 EXPECT_EQ(1, observer.GetChangedCountAndClear()); |
| 937 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); |
| 938 |
| 907 table_->set_observer(NULL); | 939 table_->set_observer(NULL); |
| 908 } | 940 } |
| 909 | 941 |
| 910 // Verifies multiple selection gestures work (control-click, shift-click ...). | 942 // Verifies multiple selection gestures work (control-click, shift-click ...). |
| 911 TEST_F(TableViewTest, Multiselection) { | 943 TEST_F(TableViewTest, Multiselection) { |
| 912 // Configure the grouper so that there are three groups: | 944 // Configure the grouper so that there are three groups: |
| 913 // A 0 | 945 // A 0 |
| 914 // 1 | 946 // 1 |
| 915 // B 5 | 947 // B 5 |
| 916 // C 2 | 948 // C 2 |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1019 new_selection.set_active(0); | 1051 new_selection.set_active(0); |
| 1020 new_selection.set_anchor(0); | 1052 new_selection.set_anchor(0); |
| 1021 helper_->SetSelectionModel(new_selection); | 1053 helper_->SetSelectionModel(new_selection); |
| 1022 model_->RemoveRow(0); | 1054 model_->RemoveRow(0); |
| 1023 helper_->OnFocus(); | 1055 helper_->OnFocus(); |
| 1024 } | 1056 } |
| 1025 | 1057 |
| 1026 // Tests that focusing the table will activate the first row, but only if | 1058 // Tests that focusing the table will activate the first row, but only if |
| 1027 // there's no active row. | 1059 // there's no active row. |
| 1028 TEST_F(TableViewTest, InitialFocusActivatesFirstRow) { | 1060 TEST_F(TableViewTest, InitialFocusActivatesFirstRow) { |
| 1061 // Focus selects the first row if there's nothing selected. |
| 1029 EXPECT_EQ(-1, table_->selection_model().active()); | 1062 EXPECT_EQ(-1, table_->selection_model().active()); |
| 1030 helper_->OnFocus(); | 1063 helper_->OnFocus(); |
| 1064 base::RunLoop().RunUntilIdle(); |
| 1031 EXPECT_EQ(0, table_->selection_model().active()); | 1065 EXPECT_EQ(0, table_->selection_model().active()); |
| 1032 | 1066 |
| 1067 // Focus does not affect selection if something's already selected. |
| 1033 ui::ListSelectionModel new_selection; | 1068 ui::ListSelectionModel new_selection; |
| 1034 new_selection.set_active(1); | 1069 new_selection.set_active(1); |
| 1035 helper_->SetSelectionModel(new_selection); | 1070 helper_->SetSelectionModel(new_selection); |
| 1036 EXPECT_EQ(1, table_->selection_model().active()); | 1071 EXPECT_EQ(1, table_->selection_model().active()); |
| 1037 helper_->OnFocus(); | 1072 helper_->OnFocus(); |
| 1073 base::RunLoop().RunUntilIdle(); |
| 1074 EXPECT_EQ(1, table_->selection_model().active()); |
| 1075 |
| 1076 // Focus does not affect selection if something gets selected synchronously |
| 1077 // after focus is taken. |
| 1078 new_selection.Clear(); |
| 1079 helper_->SetSelectionModel(new_selection); |
| 1080 EXPECT_EQ(-1, table_->selection_model().active()); |
| 1081 helper_->OnFocus(); |
| 1082 new_selection.set_active(1); |
| 1083 helper_->SetSelectionModel(new_selection); |
| 1084 EXPECT_EQ(1, table_->selection_model().active()); |
| 1085 base::RunLoop().RunUntilIdle(); |
| 1038 EXPECT_EQ(1, table_->selection_model().active()); | 1086 EXPECT_EQ(1, table_->selection_model().active()); |
| 1039 | 1087 |
| 1040 // Remove all rows; focusing should not activate a non existent first row. | 1088 // Remove all rows; focusing should not activate a non existent first row. |
| 1041 while (model_->RowCount()) | 1089 while (model_->RowCount()) |
| 1042 model_->RemoveRow(0); | 1090 model_->RemoveRow(0); |
| 1043 | 1091 |
| 1044 new_selection.set_active(-1); | 1092 new_selection.set_active(-1); |
| 1045 helper_->SetSelectionModel(new_selection); | 1093 helper_->SetSelectionModel(new_selection); |
| 1046 helper_->OnFocus(); | 1094 helper_->OnFocus(); |
| 1047 EXPECT_EQ(-1, table_->selection_model().active()); | 1095 EXPECT_EQ(-1, table_->selection_model().active()); |
| 1048 } | 1096 } |
| 1049 | 1097 |
| 1050 } // namespace views | 1098 } // namespace views |
| OLD | NEW |