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 3 // found in the LICENSE file. 4 5 #include "ui/views/controls/table/table_view.h" 6 7 #include "base/strings/string_number_conversions.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "testing/gtest/include/gtest/gtest.h" 10 #include "ui/views/controls/table/table_grouper.h" 11 #include "ui/views/controls/table/table_header.h" 12 #include "ui/views/controls/table/table_view_observer.h" 13 14 // Put the tests in the views namespace to make it easier to declare them as 15 // friend classes. 16 namespace views { 17 18 class TableViewTestHelper { 19 public: 20 explicit TableViewTestHelper(TableView* table) : table_(table) {} 21 22 std::string GetPaintRegion(const gfx::Rect& bounds) { 23 TableView::PaintRegion region(table_->GetPaintRegion(bounds)); 24 return "rows=" + base::IntToString(region.min_row) + " " + 25 base::IntToString(region.max_row) + " cols=" + 26 base::IntToString(region.min_column) + " " + 27 base::IntToString(region.max_column); 28 } 29 30 size_t visible_col_count() { 31 return table_->visible_columns().size(); 32 } 33 34 TableHeader* header() { return table_->header_; } 35 36 private: 37 TableView* table_; 38 39 DISALLOW_COPY_AND_ASSIGN(TableViewTestHelper); 40 }; 41 42 namespace { 43 44 // TestTableModel2 ------------------------------------------------------------- 45 46 // Trivial TableModel implementation that is backed by a vector of vectors. 47 // Provides methods for adding/removing/changing the contents that notify the 48 // observer appropriately. 49 // 50 // Initial contents are: 51 // 0, 1 52 // 1, 1 53 // 2, 2 54 // 3, 0 55 class TestTableModel2 : public ui::TableModel { 56 public: 57 TestTableModel2(); 58 59 // Adds a new row at index |row| with values |c1_value| and |c2_value|. 60 void AddRow(int row, int c1_value, int c2_value); 61 62 // Removes the row at index |row|. 63 void RemoveRow(int row); 64 65 // Changes the values of the row at |row|. 66 void ChangeRow(int row, int c1_value, int c2_value); 67 68 // ui::TableModel: 69 virtual int RowCount() OVERRIDE; 70 virtual base::string16 GetText(int row, int column_id) OVERRIDE; 71 virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE; 72 virtual int CompareValues(int row1, int row2, int column_id) OVERRIDE; 73 74 private: 75 ui::TableModelObserver* observer_; 76 77 // The data. 78 std::vector<std::vector<int> > rows_; 79 80 DISALLOW_COPY_AND_ASSIGN(TestTableModel2); 81 }; 82 83 TestTableModel2::TestTableModel2() : observer_(NULL) { 84 AddRow(0, 0, 1); 85 AddRow(1, 1, 1); 86 AddRow(2, 2, 2); 87 AddRow(3, 3, 0); 88 } 89 90 void TestTableModel2::AddRow(int row, int c1_value, int c2_value) { 91 DCHECK(row >= 0 && row <= static_cast<int>(rows_.size())); 92 std::vector<int> new_row; 93 new_row.push_back(c1_value); 94 new_row.push_back(c2_value); 95 rows_.insert(rows_.begin() + row, new_row); 96 if (observer_) 97 observer_->OnItemsAdded(row, 1); 98 } 99 void TestTableModel2::RemoveRow(int row) { 100 DCHECK(row >= 0 && row <= static_cast<int>(rows_.size())); 101 rows_.erase(rows_.begin() + row); 102 if (observer_) 103 observer_->OnItemsRemoved(row, 1); 104 } 105 106 void TestTableModel2::ChangeRow(int row, int c1_value, int c2_value) { 107 DCHECK(row >= 0 && row < static_cast<int>(rows_.size())); 108 rows_[row][0] = c1_value; 109 rows_[row][1] = c2_value; 110 if (observer_) 111 observer_->OnItemsChanged(row, 1); 112 } 113 114 int TestTableModel2::RowCount() { 115 return static_cast<int>(rows_.size()); 116 } 117 118 base::string16 TestTableModel2::GetText(int row, int column_id) { 119 return base::IntToString16(rows_[row][column_id]); 120 } 121 122 void TestTableModel2::SetObserver(ui::TableModelObserver* observer) { 123 observer_ = observer; 124 } 125 126 int TestTableModel2::CompareValues(int row1, int row2, int column_id) { 127 return rows_[row1][column_id] - rows_[row2][column_id]; 128 } 129 130 // Returns the view to model mapping as a string. 131 std::string GetViewToModelAsString(TableView* table) { 132 std::string result; 133 for (int i = 0; i < table->RowCount(); ++i) { 134 if (i != 0) 135 result += " "; 136 result += base::IntToString(table->ViewToModel(i)); 137 } 138 return result; 139 } 140 141 // Returns the model to view mapping as a string. 142 std::string GetModelToViewAsString(TableView* table) { 143 std::string result; 144 for (int i = 0; i < table->RowCount(); ++i) { 145 if (i != 0) 146 result += " "; 147 result += base::IntToString(table->ModelToView(i)); 148 } 149 return result; 150 } 151 152 class TestTableView : public TableView { 153 public: 154 TestTableView(ui::TableModel* model, 155 const std::vector<ui::TableColumn>& columns) 156 : TableView(model, columns, TEXT_ONLY, false) { 157 } 158 159 // View overrides: 160 virtual bool HasFocus() const OVERRIDE { 161 // Overriden so key processing works. 162 return true; 163 } 164 165 private: 166 DISALLOW_COPY_AND_ASSIGN(TestTableView); 167 }; 168 169 } // namespace 170 171 class TableViewTest : public testing::Test { 172 public: 173 TableViewTest() : table_(NULL) {} 174 175 virtual void SetUp() OVERRIDE { 176 model_.reset(new TestTableModel2); 177 std::vector<ui::TableColumn> columns(2); 178 columns[0].title = base::ASCIIToUTF16("Title Column 0"); 179 columns[0].sortable = true; 180 columns[1].title = base::ASCIIToUTF16("Title Column 1"); 181 columns[1].id = 1; 182 columns[1].sortable = true; 183 table_ = new TestTableView(model_.get(), columns); 184 parent_.reset(table_->CreateParentIfNecessary()); 185 parent_->SetBounds(0, 0, 10000, 10000); 186 parent_->Layout(); 187 helper_.reset(new TableViewTestHelper(table_)); 188 } 189 190 void ClickOnRow(int row, int flags) { 191 const int y = row * table_->row_height(); 192 const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(0, y), 193 gfx::Point(0, y), 194 ui::EF_LEFT_MOUSE_BUTTON | flags, 195 ui::EF_LEFT_MOUSE_BUTTON); 196 table_->OnMousePressed(pressed); 197 } 198 199 void TapOnRow(int row) { 200 const int y = row * table_->row_height(); 201 const ui::GestureEventDetails event_details(ui::ET_GESTURE_TAP, 202 .0f, .0f); 203 ui::GestureEvent tap(ui::ET_GESTURE_TAP, 0, y, 0, base::TimeDelta(), 204 event_details, 1); 205 table_->OnGestureEvent(&tap); 206 } 207 208 // Returns the state of the selection model as a string. The format is: 209 // 'active=X anchor=X selection=X X X...'. 210 std::string SelectionStateAsString() const { 211 const ui::ListSelectionModel& model(table_->selection_model()); 212 std::string result = "active=" + base::IntToString(model.active()) + 213 " anchor=" + base::IntToString(model.anchor()) + 214 " selection="; 215 const ui::ListSelectionModel::SelectedIndices& selection( 216 model.selected_indices()); 217 for (size_t i = 0; i < selection.size(); ++i) { 218 if (i != 0) 219 result += " "; 220 result += base::IntToString(selection[i]); 221 } 222 return result; 223 } 224 225 void PressKey(ui::KeyboardCode code) { 226 ui::KeyEvent event(ui::ET_KEY_PRESSED, code, 0, false); 227 table_->OnKeyPressed(event); 228 } 229 230 protected: 231 scoped_ptr<TestTableModel2> model_; 232 233 // Owned by |parent_|. 234 TableView* table_; 235 236 scoped_ptr<TableViewTestHelper> helper_; 237 238 private: 239 scoped_ptr<View> parent_; 240 241 DISALLOW_COPY_AND_ASSIGN(TableViewTest); 242 }; 243 244 // Verifies GetPaintRegion. 245 TEST_F(TableViewTest, GetPaintRegion) { 246 // Two columns should be visible. 247 EXPECT_EQ(2u, helper_->visible_col_count()); 248 249 EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds())); 250 EXPECT_EQ("rows=0 4 cols=0 1", 251 helper_->GetPaintRegion(gfx::Rect(0, 0, 1, table_->height()))); 252 } 253 254 // Verifies SetColumnVisibility(). 255 TEST_F(TableViewTest, ColumnVisibility) { 256 // Two columns should be visible. 257 EXPECT_EQ(2u, helper_->visible_col_count()); 258 259 // Should do nothing (column already visible). 260 table_->SetColumnVisibility(0, true); 261 EXPECT_EQ(2u, helper_->visible_col_count()); 262 263 // Hide the first column. 264 table_->SetColumnVisibility(0, false); 265 ASSERT_EQ(1u, helper_->visible_col_count()); 266 EXPECT_EQ(1, table_->visible_columns()[0].column.id); 267 EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds())); 268 269 // Hide the second column. 270 table_->SetColumnVisibility(1, false); 271 EXPECT_EQ(0u, helper_->visible_col_count()); 272 273 // Show the second column. 274 table_->SetColumnVisibility(1, true); 275 ASSERT_EQ(1u, helper_->visible_col_count()); 276 EXPECT_EQ(1, table_->visible_columns()[0].column.id); 277 EXPECT_EQ("rows=0 4 cols=0 1", helper_->GetPaintRegion(table_->bounds())); 278 279 // Show the first column. 280 table_->SetColumnVisibility(0, true); 281 ASSERT_EQ(2u, helper_->visible_col_count()); 282 EXPECT_EQ(1, table_->visible_columns()[0].column.id); 283 EXPECT_EQ(0, table_->visible_columns()[1].column.id); 284 EXPECT_EQ("rows=0 4 cols=0 2", helper_->GetPaintRegion(table_->bounds())); 285 } 286 287 // Verifies resizing a column works. 288 TEST_F(TableViewTest, Resize) { 289 const int x = table_->visible_columns()[0].width; 290 EXPECT_NE(0, x); 291 // Drag the mouse 1 pixel to the left. 292 const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0), 293 gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON, 294 ui::EF_LEFT_MOUSE_BUTTON); 295 helper_->header()->OnMousePressed(pressed); 296 const ui::MouseEvent dragged(ui::ET_MOUSE_DRAGGED, gfx::Point(x - 1, 0), 297 gfx::Point(x - 1, 0), ui::EF_LEFT_MOUSE_BUTTON, 298 0); 299 helper_->header()->OnMouseDragged(dragged); 300 301 // This should shrink the first column and pull the second column in. 302 EXPECT_EQ(x - 1, table_->visible_columns()[0].width); 303 EXPECT_EQ(x - 1, table_->visible_columns()[1].x); 304 } 305 306 // Verifies resizing a column works with a gesture. 307 TEST_F(TableViewTest, ResizeViaGesture) { 308 const int x = table_->visible_columns()[0].width; 309 EXPECT_NE(0, x); 310 // Drag the mouse 1 pixel to the left. 311 const ui::GestureEventDetails event_details(ui::ET_GESTURE_SCROLL_BEGIN, 312 .0f, .0f); 313 ui::GestureEvent scroll_begin(ui::ET_GESTURE_SCROLL_BEGIN, x, 0, 0, 314 base::TimeDelta(), event_details, 1); 315 helper_->header()->OnGestureEvent(&scroll_begin); 316 ui::GestureEvent scroll_update(ui::ET_GESTURE_SCROLL_UPDATE, x - 1, 0, 317 0, base::TimeDelta(), event_details, 1); 318 helper_->header()->OnGestureEvent(&scroll_update); 319 320 // This should shrink the first column and pull the second column in. 321 EXPECT_EQ(x - 1, table_->visible_columns()[0].width); 322 EXPECT_EQ(x - 1, table_->visible_columns()[1].x); 323 } 324 325 // Assertions for table sorting. 326 TEST_F(TableViewTest, Sort) { 327 // Toggle the sort order of the first column, shouldn't change anything. 328 table_->ToggleSortOrder(0); 329 ASSERT_EQ(1u, table_->sort_descriptors().size()); 330 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 331 EXPECT_TRUE(table_->sort_descriptors()[0].ascending); 332 EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_)); 333 EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_)); 334 335 // Invert the sort (first column descending). 336 table_->ToggleSortOrder(0); 337 ASSERT_EQ(1u, table_->sort_descriptors().size()); 338 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 339 EXPECT_FALSE(table_->sort_descriptors()[0].ascending); 340 EXPECT_EQ("3 2 1 0", GetViewToModelAsString(table_)); 341 EXPECT_EQ("3 2 1 0", GetModelToViewAsString(table_)); 342 343 // Change cell 0x3 to -1, meaning we have 0, 1, 2, -1 (in the first column). 344 model_->ChangeRow(3, -1, 0); 345 ASSERT_EQ(1u, table_->sort_descriptors().size()); 346 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 347 EXPECT_FALSE(table_->sort_descriptors()[0].ascending); 348 EXPECT_EQ("2 1 0 3", GetViewToModelAsString(table_)); 349 EXPECT_EQ("2 1 0 3", GetModelToViewAsString(table_)); 350 351 // Invert sort again (first column ascending). 352 table_->ToggleSortOrder(0); 353 ASSERT_EQ(1u, table_->sort_descriptors().size()); 354 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 355 EXPECT_TRUE(table_->sort_descriptors()[0].ascending); 356 EXPECT_EQ("3 0 1 2", GetViewToModelAsString(table_)); 357 EXPECT_EQ("1 2 3 0", GetModelToViewAsString(table_)); 358 359 // Add a row so that model has 0, 3, 1, 2, -1. 360 model_->AddRow(1, 3, 4); 361 ASSERT_EQ(1u, table_->sort_descriptors().size()); 362 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 363 EXPECT_TRUE(table_->sort_descriptors()[0].ascending); 364 EXPECT_EQ("4 0 2 3 1", GetViewToModelAsString(table_)); 365 EXPECT_EQ("1 4 2 3 0", GetModelToViewAsString(table_)); 366 367 // Delete the first row, ending up with 3, 1, 2, -1. 368 model_->RemoveRow(0); 369 ASSERT_EQ(1u, table_->sort_descriptors().size()); 370 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 371 EXPECT_TRUE(table_->sort_descriptors()[0].ascending); 372 EXPECT_EQ("3 1 2 0", GetViewToModelAsString(table_)); 373 EXPECT_EQ("3 1 2 0", GetModelToViewAsString(table_)); 374 } 375 376 // Verfies clicking on the header sorts. 377 TEST_F(TableViewTest, SortOnMouse) { 378 EXPECT_TRUE(table_->sort_descriptors().empty()); 379 380 const int x = table_->visible_columns()[0].width / 2; 381 EXPECT_NE(0, x); 382 // Press and release the mouse. 383 const ui::MouseEvent pressed(ui::ET_MOUSE_PRESSED, gfx::Point(x, 0), 384 gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON, 385 ui::EF_LEFT_MOUSE_BUTTON); 386 // The header must return true, else it won't normally get the release. 387 EXPECT_TRUE(helper_->header()->OnMousePressed(pressed)); 388 const ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(x, 0), 389 gfx::Point(x, 0), ui::EF_LEFT_MOUSE_BUTTON, 390 ui::EF_LEFT_MOUSE_BUTTON); 391 helper_->header()->OnMouseReleased(release); 392 393 ASSERT_EQ(1u, table_->sort_descriptors().size()); 394 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 395 EXPECT_TRUE(table_->sort_descriptors()[0].ascending); 396 } 397 398 namespace { 399 400 class TableGrouperImpl : public TableGrouper { 401 public: 402 TableGrouperImpl() {} 403 404 void SetRanges(const std::vector<int>& ranges) { 405 ranges_ = ranges; 406 } 407 408 // TableGrouper overrides: 409 virtual void GetGroupRange(int model_index, GroupRange* range) OVERRIDE { 410 int offset = 0; 411 size_t range_index = 0; 412 for (; range_index < ranges_.size() && offset < model_index; ++range_index) 413 offset += ranges_[range_index]; 414 415 if (offset == model_index) { 416 range->start = model_index; 417 range->length = ranges_[range_index]; 418 } else { 419 range->start = offset - ranges_[range_index - 1]; 420 range->length = ranges_[range_index - 1]; 421 } 422 } 423 424 private: 425 std::vector<int> ranges_; 426 427 DISALLOW_COPY_AND_ASSIGN(TableGrouperImpl); 428 }; 429 430 } // namespace 431 432 // Assertions around grouping. 433 TEST_F(TableViewTest, Grouping) { 434 // Configure the grouper so that there are two groups: 435 // A 0 436 // 1 437 // B 2 438 // 3 439 TableGrouperImpl grouper; 440 std::vector<int> ranges; 441 ranges.push_back(2); 442 ranges.push_back(2); 443 grouper.SetRanges(ranges); 444 table_->SetGrouper(&grouper); 445 446 // Toggle the sort order of the first column, shouldn't change anything. 447 table_->ToggleSortOrder(0); 448 ASSERT_EQ(1u, table_->sort_descriptors().size()); 449 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 450 EXPECT_TRUE(table_->sort_descriptors()[0].ascending); 451 EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_)); 452 EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_)); 453 454 // Sort descending, resulting: 455 // B 2 456 // 3 457 // A 0 458 // 1 459 table_->ToggleSortOrder(0); 460 ASSERT_EQ(1u, table_->sort_descriptors().size()); 461 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 462 EXPECT_FALSE(table_->sort_descriptors()[0].ascending); 463 EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_)); 464 EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_)); 465 466 // Change the entry in the 4th row to -1. The model now becomes: 467 // A 0 468 // 1 469 // B 2 470 // -1 471 // Since the first entry in the range didn't change the sort isn't impacted. 472 model_->ChangeRow(3, -1, 0); 473 ASSERT_EQ(1u, table_->sort_descriptors().size()); 474 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 475 EXPECT_FALSE(table_->sort_descriptors()[0].ascending); 476 EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_)); 477 EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_)); 478 479 // Change the entry in the 3rd row to -1. The model now becomes: 480 // A 0 481 // 1 482 // B -1 483 // -1 484 model_->ChangeRow(2, -1, 0); 485 ASSERT_EQ(1u, table_->sort_descriptors().size()); 486 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 487 EXPECT_FALSE(table_->sort_descriptors()[0].ascending); 488 EXPECT_EQ("0 1 2 3", GetViewToModelAsString(table_)); 489 EXPECT_EQ("0 1 2 3", GetModelToViewAsString(table_)); 490 491 // Toggle to ascending sort. 492 table_->ToggleSortOrder(0); 493 ASSERT_EQ(1u, table_->sort_descriptors().size()); 494 EXPECT_EQ(0, table_->sort_descriptors()[0].column_id); 495 EXPECT_TRUE(table_->sort_descriptors()[0].ascending); 496 EXPECT_EQ("2 3 0 1", GetViewToModelAsString(table_)); 497 EXPECT_EQ("2 3 0 1", GetModelToViewAsString(table_)); 498 } 499 500 namespace { 501 502 class TableViewObserverImpl : public TableViewObserver { 503 public: 504 TableViewObserverImpl() : selection_changed_count_(0) {} 505 506 int GetChangedCountAndClear() { 507 const int count = selection_changed_count_; 508 selection_changed_count_ = 0; 509 return count; 510 } 511 512 // TableViewObserver overrides: 513 virtual void OnSelectionChanged() OVERRIDE { 514 selection_changed_count_++; 515 } 516 517 private: 518 int selection_changed_count_; 519 520 DISALLOW_COPY_AND_ASSIGN(TableViewObserverImpl); 521 }; 522 523 } // namespace 524 525 // Assertions around changing the selection. 526 TEST_F(TableViewTest, Selection) { 527 TableViewObserverImpl observer; 528 table_->SetObserver(&observer); 529 530 // Initially no selection. 531 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); 532 533 // Select the last row. 534 table_->Select(3); 535 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 536 EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString()); 537 538 // Change sort, shouldn't notify of change (toggle twice so that order 539 // actually changes). 540 table_->ToggleSortOrder(0); 541 table_->ToggleSortOrder(0); 542 EXPECT_EQ(0, observer.GetChangedCountAndClear()); 543 EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString()); 544 545 // Remove the selected row, this should notify of a change and update the 546 // selection. 547 model_->RemoveRow(3); 548 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 549 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString()); 550 551 // Insert a row, since the selection in terms of the original model hasn't 552 // changed the observer is not notified. 553 model_->AddRow(0, 1, 2); 554 EXPECT_EQ(0, observer.GetChangedCountAndClear()); 555 EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString()); 556 557 table_->SetObserver(NULL); 558 } 559 560 // Verifies selection works by way of a gesture. 561 TEST_F(TableViewTest, SelectOnTap) { 562 // Initially no selection. 563 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); 564 565 TableViewObserverImpl observer; 566 table_->SetObserver(&observer); 567 568 // Click on the first row, should select it. 569 TapOnRow(0); 570 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 571 EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString()); 572 573 table_->SetObserver(NULL); 574 } 575 576 // Verifies up/down correctly navigates through groups. 577 TEST_F(TableViewTest, KeyUpDown) { 578 // Configure the grouper so that there are three groups: 579 // A 0 580 // 1 581 // B 5 582 // C 2 583 // 3 584 model_->AddRow(2, 5, 0); 585 TableGrouperImpl grouper; 586 std::vector<int> ranges; 587 ranges.push_back(2); 588 ranges.push_back(1); 589 ranges.push_back(2); 590 grouper.SetRanges(ranges); 591 table_->SetGrouper(&grouper); 592 593 TableViewObserverImpl observer; 594 table_->SetObserver(&observer); 595 596 // Initially no selection. 597 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); 598 599 PressKey(ui::VKEY_DOWN); 600 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 601 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); 602 603 PressKey(ui::VKEY_DOWN); 604 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 605 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString()); 606 607 PressKey(ui::VKEY_DOWN); 608 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 609 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString()); 610 611 PressKey(ui::VKEY_DOWN); 612 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 613 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString()); 614 615 PressKey(ui::VKEY_DOWN); 616 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 617 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); 618 619 PressKey(ui::VKEY_DOWN); 620 EXPECT_EQ(0, observer.GetChangedCountAndClear()); 621 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); 622 623 PressKey(ui::VKEY_UP); 624 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 625 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString()); 626 627 PressKey(ui::VKEY_UP); 628 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 629 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString()); 630 631 PressKey(ui::VKEY_UP); 632 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 633 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString()); 634 635 PressKey(ui::VKEY_UP); 636 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 637 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); 638 639 PressKey(ui::VKEY_UP); 640 EXPECT_EQ(0, observer.GetChangedCountAndClear()); 641 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); 642 643 // Sort the table descending by column 1, view now looks like: 644 // B 5 model: 2 645 // C 2 3 646 // 3 4 647 // A 0 0 648 // 1 1 649 table_->ToggleSortOrder(0); 650 table_->ToggleSortOrder(0); 651 652 EXPECT_EQ("2 3 4 0 1", GetViewToModelAsString(table_)); 653 654 table_->Select(-1); 655 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); 656 657 observer.GetChangedCountAndClear(); 658 // Up with nothing selected selects the first row. 659 PressKey(ui::VKEY_UP); 660 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 661 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString()); 662 663 PressKey(ui::VKEY_DOWN); 664 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 665 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString()); 666 667 PressKey(ui::VKEY_DOWN); 668 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 669 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); 670 671 PressKey(ui::VKEY_DOWN); 672 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 673 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); 674 675 PressKey(ui::VKEY_DOWN); 676 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 677 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString()); 678 679 PressKey(ui::VKEY_DOWN); 680 EXPECT_EQ(0, observer.GetChangedCountAndClear()); 681 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString()); 682 683 PressKey(ui::VKEY_UP); 684 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 685 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); 686 687 PressKey(ui::VKEY_UP); 688 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 689 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); 690 691 PressKey(ui::VKEY_UP); 692 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 693 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString()); 694 695 PressKey(ui::VKEY_UP); 696 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 697 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString()); 698 699 PressKey(ui::VKEY_UP); 700 EXPECT_EQ(0, observer.GetChangedCountAndClear()); 701 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString()); 702 703 table_->SetObserver(NULL); 704 } 705 706 // Verifies home/end do the right thing. 707 TEST_F(TableViewTest, HomeEnd) { 708 // Configure the grouper so that there are three groups: 709 // A 0 710 // 1 711 // B 5 712 // C 2 713 // 3 714 model_->AddRow(2, 5, 0); 715 TableGrouperImpl grouper; 716 std::vector<int> ranges; 717 ranges.push_back(2); 718 ranges.push_back(1); 719 ranges.push_back(2); 720 grouper.SetRanges(ranges); 721 table_->SetGrouper(&grouper); 722 723 TableViewObserverImpl observer; 724 table_->SetObserver(&observer); 725 726 // Initially no selection. 727 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); 728 729 PressKey(ui::VKEY_HOME); 730 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 731 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); 732 733 PressKey(ui::VKEY_END); 734 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 735 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); 736 737 table_->SetObserver(NULL); 738 } 739 740 // Verifies multiple selection gestures work (control-click, shift-click ...). 741 TEST_F(TableViewTest, Multiselection) { 742 // Configure the grouper so that there are three groups: 743 // A 0 744 // 1 745 // B 5 746 // C 2 747 // 3 748 model_->AddRow(2, 5, 0); 749 TableGrouperImpl grouper; 750 std::vector<int> ranges; 751 ranges.push_back(2); 752 ranges.push_back(1); 753 ranges.push_back(2); 754 grouper.SetRanges(ranges); 755 table_->SetGrouper(&grouper); 756 757 // Initially no selection. 758 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); 759 760 TableViewObserverImpl observer; 761 table_->SetObserver(&observer); 762 763 // Click on the first row, should select it and the second row. 764 ClickOnRow(0, 0); 765 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 766 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString()); 767 768 // Click on the last row, should select it and the row before it. 769 ClickOnRow(4, 0); 770 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 771 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); 772 773 // Shift click on the third row, should extend selection to it. 774 ClickOnRow(2, ui::EF_SHIFT_DOWN); 775 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 776 EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString()); 777 778 // Control click on third row, should toggle it. 779 ClickOnRow(2, ui::EF_CONTROL_DOWN); 780 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 781 EXPECT_EQ("active=2 anchor=2 selection=3 4", SelectionStateAsString()); 782 783 // Control-shift click on second row, should extend selection to it. 784 ClickOnRow(1, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN); 785 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 786 EXPECT_EQ("active=1 anchor=2 selection=0 1 2 3 4", SelectionStateAsString()); 787 788 // Click on last row again. 789 ClickOnRow(4, 0); 790 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 791 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); 792 793 table_->SetObserver(NULL); 794 } 795 796 // Verifies multiple selection gestures work when sorted. 797 TEST_F(TableViewTest, MultiselectionWithSort) { 798 // Configure the grouper so that there are three groups: 799 // A 0 800 // 1 801 // B 5 802 // C 2 803 // 3 804 model_->AddRow(2, 5, 0); 805 TableGrouperImpl grouper; 806 std::vector<int> ranges; 807 ranges.push_back(2); 808 ranges.push_back(1); 809 ranges.push_back(2); 810 grouper.SetRanges(ranges); 811 table_->SetGrouper(&grouper); 812 813 // Sort the table descending by column 1, view now looks like: 814 // B 5 model: 2 815 // C 2 3 816 // 3 4 817 // A 0 0 818 // 1 1 819 table_->ToggleSortOrder(0); 820 table_->ToggleSortOrder(0); 821 822 // Initially no selection. 823 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString()); 824 825 TableViewObserverImpl observer; 826 table_->SetObserver(&observer); 827 828 // Click on the third row, should select it and the second row. 829 ClickOnRow(2, 0); 830 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 831 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString()); 832 833 // Extend selection to first row. 834 ClickOnRow(0, ui::EF_SHIFT_DOWN); 835 EXPECT_EQ(1, observer.GetChangedCountAndClear()); 836 EXPECT_EQ("active=2 anchor=4 selection=2 3 4", SelectionStateAsString()); 837 838 table_->SetObserver(NULL); 839 } 840 841 } // namespace views 842