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.
5 #include "ui/views/controls/table/table_view.h"
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"
14 // Put the tests in the views namespace to make it easier to declare them as
18 class TableViewTestHelper
{
20 explicit TableViewTestHelper(TableView
* table
) : table_(table
) {}
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
);
30 size_t visible_col_count() {
31 return table_
->visible_columns().size();
34 TableHeader
* header() { return table_
->header_
; }
39 DISALLOW_COPY_AND_ASSIGN(TableViewTestHelper
);
44 // TestTableModel2 -------------------------------------------------------------
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.
50 // Initial contents are:
55 class TestTableModel2
: public ui::TableModel
{
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
);
62 // Removes the row at index |row|.
63 void RemoveRow(int row
);
65 // Changes the values of the row at |row|.
66 void ChangeRow(int row
, int c1_value
, int c2_value
);
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
;
75 ui::TableModelObserver
* observer_
;
78 std::vector
<std::vector
<int> > rows_
;
80 DISALLOW_COPY_AND_ASSIGN(TestTableModel2
);
83 TestTableModel2::TestTableModel2() : observer_(NULL
) {
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
);
97 observer_
->OnItemsAdded(row
, 1);
99 void TestTableModel2::RemoveRow(int row
) {
100 DCHECK(row
>= 0 && row
<= static_cast<int>(rows_
.size()));
101 rows_
.erase(rows_
.begin() + row
);
103 observer_
->OnItemsRemoved(row
, 1);
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
;
111 observer_
->OnItemsChanged(row
, 1);
114 int TestTableModel2::RowCount() {
115 return static_cast<int>(rows_
.size());
118 base::string16
TestTableModel2::GetText(int row
, int column_id
) {
119 return base::IntToString16(rows_
[row
][column_id
]);
122 void TestTableModel2::SetObserver(ui::TableModelObserver
* observer
) {
123 observer_
= observer
;
126 int TestTableModel2::CompareValues(int row1
, int row2
, int column_id
) {
127 return rows_
[row1
][column_id
] - rows_
[row2
][column_id
];
130 // Returns the view to model mapping as a string.
131 std::string
GetViewToModelAsString(TableView
* table
) {
133 for (int i
= 0; i
< table
->RowCount(); ++i
) {
136 result
+= base::IntToString(table
->ViewToModel(i
));
141 // Returns the model to view mapping as a string.
142 std::string
GetModelToViewAsString(TableView
* table
) {
144 for (int i
= 0; i
< table
->RowCount(); ++i
) {
147 result
+= base::IntToString(table
->ModelToView(i
));
152 class TestTableView
: public TableView
{
154 TestTableView(ui::TableModel
* model
,
155 const std::vector
<ui::TableColumn
>& columns
)
156 : TableView(model
, columns
, TEXT_ONLY
, false) {
160 virtual bool HasFocus() const OVERRIDE
{
161 // Overriden so key processing works.
166 DISALLOW_COPY_AND_ASSIGN(TestTableView
);
171 class TableViewTest
: public testing::Test
{
173 TableViewTest() : table_(NULL
) {}
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");
182 columns
[1].sortable
= true;
183 table_
= new TestTableView(model_
.get(), columns
);
184 parent_
.reset(table_
->CreateParentIfNecessary());
185 parent_
->SetBounds(0, 0, 10000, 10000);
187 helper_
.reset(new TableViewTestHelper(table_
));
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
),
194 ui::EF_LEFT_MOUSE_BUTTON
| flags
,
195 ui::EF_LEFT_MOUSE_BUTTON
);
196 table_
->OnMousePressed(pressed
);
199 void TapOnRow(int row
) {
200 const int y
= row
* table_
->row_height();
201 const ui::GestureEventDetails
event_details(ui::ET_GESTURE_TAP
,
203 ui::GestureEvent
tap(ui::ET_GESTURE_TAP
, 0, y
, 0, base::TimeDelta(),
205 table_
->OnGestureEvent(&tap
);
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()) +
215 const ui::ListSelectionModel::SelectedIndices
& selection(
216 model
.selected_indices());
217 for (size_t i
= 0; i
< selection
.size(); ++i
) {
220 result
+= base::IntToString(selection
[i
]);
225 void PressKey(ui::KeyboardCode code
) {
226 ui::KeyEvent
event(ui::ET_KEY_PRESSED
, code
, 0, false);
227 table_
->OnKeyPressed(event
);
231 scoped_ptr
<TestTableModel2
> model_
;
233 // Owned by |parent_|.
236 scoped_ptr
<TableViewTestHelper
> helper_
;
239 scoped_ptr
<View
> parent_
;
241 DISALLOW_COPY_AND_ASSIGN(TableViewTest
);
244 // Verifies GetPaintRegion.
245 TEST_F(TableViewTest
, GetPaintRegion
) {
246 // Two columns should be visible.
247 EXPECT_EQ(2u, helper_
->visible_col_count());
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())));
254 // Verifies SetColumnVisibility().
255 TEST_F(TableViewTest
, ColumnVisibility
) {
256 // Two columns should be visible.
257 EXPECT_EQ(2u, helper_
->visible_col_count());
259 // Should do nothing (column already visible).
260 table_
->SetColumnVisibility(0, true);
261 EXPECT_EQ(2u, helper_
->visible_col_count());
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()));
269 // Hide the second column.
270 table_
->SetColumnVisibility(1, false);
271 EXPECT_EQ(0u, helper_
->visible_col_count());
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()));
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()));
287 // Verifies resizing a column works.
288 TEST_F(TableViewTest
, Resize
) {
289 const int x
= table_
->visible_columns()[0].width
;
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
,
299 helper_
->header()->OnMouseDragged(dragged
);
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
);
306 // Verifies resizing a column works with a gesture.
307 TEST_F(TableViewTest
, ResizeViaGesture
) {
308 const int x
= table_
->visible_columns()[0].width
;
310 // Drag the mouse 1 pixel to the left.
311 const ui::GestureEventDetails
event_details(ui::ET_GESTURE_SCROLL_BEGIN
,
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
);
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
);
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_
));
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_
));
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_
));
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_
));
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_
));
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_
));
376 // Verfies clicking on the header sorts.
377 TEST_F(TableViewTest
, SortOnMouse
) {
378 EXPECT_TRUE(table_
->sort_descriptors().empty());
380 const int x
= table_
->visible_columns()[0].width
/ 2;
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
);
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
);
400 class TableGrouperImpl
: public TableGrouper
{
402 TableGrouperImpl() {}
404 void SetRanges(const std::vector
<int>& ranges
) {
408 // TableGrouper overrides:
409 virtual void GetGroupRange(int model_index
, GroupRange
* range
) OVERRIDE
{
411 size_t range_index
= 0;
412 for (; range_index
< ranges_
.size() && offset
< model_index
; ++range_index
)
413 offset
+= ranges_
[range_index
];
415 if (offset
== model_index
) {
416 range
->start
= model_index
;
417 range
->length
= ranges_
[range_index
];
419 range
->start
= offset
- ranges_
[range_index
- 1];
420 range
->length
= ranges_
[range_index
- 1];
425 std::vector
<int> ranges_
;
427 DISALLOW_COPY_AND_ASSIGN(TableGrouperImpl
);
432 // Assertions around grouping.
433 TEST_F(TableViewTest
, Grouping
) {
434 // Configure the grouper so that there are two groups:
439 TableGrouperImpl grouper
;
440 std::vector
<int> ranges
;
443 grouper
.SetRanges(ranges
);
444 table_
->SetGrouper(&grouper
);
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_
));
454 // Sort descending, resulting:
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_
));
466 // Change the entry in the 4th row to -1. The model now becomes:
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_
));
479 // Change the entry in the 3rd row to -1. The model now becomes:
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_
));
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_
));
502 class TableViewObserverImpl
: public TableViewObserver
{
504 TableViewObserverImpl() : selection_changed_count_(0) {}
506 int GetChangedCountAndClear() {
507 const int count
= selection_changed_count_
;
508 selection_changed_count_
= 0;
512 // TableViewObserver overrides:
513 virtual void OnSelectionChanged() OVERRIDE
{
514 selection_changed_count_
++;
518 int selection_changed_count_
;
520 DISALLOW_COPY_AND_ASSIGN(TableViewObserverImpl
);
525 // Assertions around changing the selection.
526 TEST_F(TableViewTest
, Selection
) {
527 TableViewObserverImpl observer
;
528 table_
->SetObserver(&observer
);
530 // Initially no selection.
531 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
533 // Select the last row.
535 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
536 EXPECT_EQ("active=3 anchor=3 selection=3", SelectionStateAsString());
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());
545 // Remove the selected row, this should notify of a change and update the
547 model_
->RemoveRow(3);
548 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
549 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
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());
557 table_
->SetObserver(NULL
);
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());
565 TableViewObserverImpl observer
;
566 table_
->SetObserver(&observer
);
568 // Click on the first row, should select it.
570 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
571 EXPECT_EQ("active=0 anchor=0 selection=0", SelectionStateAsString());
573 table_
->SetObserver(NULL
);
576 // Verifies up/down correctly navigates through groups.
577 TEST_F(TableViewTest
, KeyUpDown
) {
578 // Configure the grouper so that there are three groups:
584 model_
->AddRow(2, 5, 0);
585 TableGrouperImpl grouper
;
586 std::vector
<int> ranges
;
590 grouper
.SetRanges(ranges
);
591 table_
->SetGrouper(&grouper
);
593 TableViewObserverImpl observer
;
594 table_
->SetObserver(&observer
);
596 // Initially no selection.
597 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
599 PressKey(ui::VKEY_DOWN
);
600 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
601 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
603 PressKey(ui::VKEY_DOWN
);
604 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
605 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
607 PressKey(ui::VKEY_DOWN
);
608 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
609 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
611 PressKey(ui::VKEY_DOWN
);
612 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
613 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
615 PressKey(ui::VKEY_DOWN
);
616 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
617 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
619 PressKey(ui::VKEY_DOWN
);
620 EXPECT_EQ(0, observer
.GetChangedCountAndClear());
621 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
623 PressKey(ui::VKEY_UP
);
624 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
625 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
627 PressKey(ui::VKEY_UP
);
628 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
629 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
631 PressKey(ui::VKEY_UP
);
632 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
633 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
635 PressKey(ui::VKEY_UP
);
636 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
637 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
639 PressKey(ui::VKEY_UP
);
640 EXPECT_EQ(0, observer
.GetChangedCountAndClear());
641 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
643 // Sort the table descending by column 1, view now looks like:
649 table_
->ToggleSortOrder(0);
650 table_
->ToggleSortOrder(0);
652 EXPECT_EQ("2 3 4 0 1", GetViewToModelAsString(table_
));
655 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
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());
663 PressKey(ui::VKEY_DOWN
);
664 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
665 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
667 PressKey(ui::VKEY_DOWN
);
668 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
669 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
671 PressKey(ui::VKEY_DOWN
);
672 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
673 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
675 PressKey(ui::VKEY_DOWN
);
676 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
677 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
679 PressKey(ui::VKEY_DOWN
);
680 EXPECT_EQ(0, observer
.GetChangedCountAndClear());
681 EXPECT_EQ("active=1 anchor=1 selection=0 1", SelectionStateAsString());
683 PressKey(ui::VKEY_UP
);
684 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
685 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
687 PressKey(ui::VKEY_UP
);
688 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
689 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
691 PressKey(ui::VKEY_UP
);
692 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
693 EXPECT_EQ("active=3 anchor=3 selection=3 4", SelectionStateAsString());
695 PressKey(ui::VKEY_UP
);
696 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
697 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
699 PressKey(ui::VKEY_UP
);
700 EXPECT_EQ(0, observer
.GetChangedCountAndClear());
701 EXPECT_EQ("active=2 anchor=2 selection=2", SelectionStateAsString());
703 table_
->SetObserver(NULL
);
706 // Verifies home/end do the right thing.
707 TEST_F(TableViewTest
, HomeEnd
) {
708 // Configure the grouper so that there are three groups:
714 model_
->AddRow(2, 5, 0);
715 TableGrouperImpl grouper
;
716 std::vector
<int> ranges
;
720 grouper
.SetRanges(ranges
);
721 table_
->SetGrouper(&grouper
);
723 TableViewObserverImpl observer
;
724 table_
->SetObserver(&observer
);
726 // Initially no selection.
727 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
729 PressKey(ui::VKEY_HOME
);
730 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
731 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
733 PressKey(ui::VKEY_END
);
734 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
735 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
737 table_
->SetObserver(NULL
);
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:
748 model_
->AddRow(2, 5, 0);
749 TableGrouperImpl grouper
;
750 std::vector
<int> ranges
;
754 grouper
.SetRanges(ranges
);
755 table_
->SetGrouper(&grouper
);
757 // Initially no selection.
758 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
760 TableViewObserverImpl observer
;
761 table_
->SetObserver(&observer
);
763 // Click on the first row, should select it and the second row.
765 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
766 EXPECT_EQ("active=0 anchor=0 selection=0 1", SelectionStateAsString());
768 // Click on the last row, should select it and the row before it.
770 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
771 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
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());
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());
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());
788 // Click on last row again.
790 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
791 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
793 table_
->SetObserver(NULL
);
796 // Verifies multiple selection gestures work when sorted.
797 TEST_F(TableViewTest
, MultiselectionWithSort
) {
798 // Configure the grouper so that there are three groups:
804 model_
->AddRow(2, 5, 0);
805 TableGrouperImpl grouper
;
806 std::vector
<int> ranges
;
810 grouper
.SetRanges(ranges
);
811 table_
->SetGrouper(&grouper
);
813 // Sort the table descending by column 1, view now looks like:
819 table_
->ToggleSortOrder(0);
820 table_
->ToggleSortOrder(0);
822 // Initially no selection.
823 EXPECT_EQ("active=-1 anchor=-1 selection=", SelectionStateAsString());
825 TableViewObserverImpl observer
;
826 table_
->SetObserver(&observer
);
828 // Click on the third row, should select it and the second row.
830 EXPECT_EQ(1, observer
.GetChangedCountAndClear());
831 EXPECT_EQ("active=4 anchor=4 selection=3 4", SelectionStateAsString());
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());
838 table_
->SetObserver(NULL
);