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/app_list/views/apps_grid_view.h"
9 #include "base/basictypes.h"
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "ui/app_list/app_list_constants.h"
18 #include "ui/app_list/app_list_folder_item.h"
19 #include "ui/app_list/app_list_item.h"
20 #include "ui/app_list/app_list_model.h"
21 #include "ui/app_list/app_list_switches.h"
22 #include "ui/app_list/pagination_model.h"
23 #include "ui/app_list/test/app_list_test_model.h"
24 #include "ui/app_list/views/app_list_item_view.h"
25 #include "ui/app_list/views/apps_grid_view_folder_delegate.h"
26 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
27 #include "ui/views/test/views_test_base.h"
36 const int kTilesPerPage
= kCols
* kRows
;
38 class PageFlipWaiter
: public PaginationModelObserver
{
40 PageFlipWaiter(base::MessageLoopForUI
* ui_loop
, PaginationModel
* model
)
41 : ui_loop_(ui_loop
), model_(model
), wait_(false) {
42 model_
->AddObserver(this);
45 ~PageFlipWaiter() override
{ model_
->RemoveObserver(this); }
55 void Reset() { selected_pages_
.clear(); }
57 const std::string
& selected_pages() const { return selected_pages_
; }
60 // PaginationModelObserver overrides:
61 void TotalPagesChanged() override
{}
62 void SelectedPageChanged(int old_selected
, int new_selected
) override
{
63 if (!selected_pages_
.empty())
64 selected_pages_
+= ',';
65 selected_pages_
+= base::IntToString(new_selected
);
70 void TransitionStarted() override
{}
71 void TransitionChanged() override
{}
73 base::MessageLoopForUI
* ui_loop_
;
74 PaginationModel
* model_
;
76 std::string selected_pages_
;
78 DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter
);
83 class AppsGridViewTest
: public views::ViewsTestBase
{
86 ~AppsGridViewTest() override
{}
88 // testing::Test overrides:
89 void SetUp() override
{
90 views::ViewsTestBase::SetUp();
91 model_
.reset(new AppListTestModel
);
92 model_
->SetFoldersEnabled(true);
94 apps_grid_view_
.reset(new AppsGridView(NULL
));
95 apps_grid_view_
->SetLayout(kCols
, kRows
);
96 apps_grid_view_
->SetBoundsRect(
97 gfx::Rect(apps_grid_view_
->GetPreferredSize()));
98 apps_grid_view_
->SetModel(model_
.get());
99 apps_grid_view_
->SetItemList(model_
->top_level_item_list());
101 test_api_
.reset(new AppsGridViewTestApi(apps_grid_view_
.get()));
103 void TearDown() override
{
104 apps_grid_view_
.reset(); // Release apps grid view before models.
105 views::ViewsTestBase::TearDown();
109 void EnsureFoldersEnabled() {
110 // Folders require AppList sync to be enabled.
111 CommandLine::ForCurrentProcess()->AppendSwitch(
112 switches::kEnableSyncAppList
);
115 AppListItemView
* GetItemViewAt(int index
) {
116 return static_cast<AppListItemView
*>(
117 test_api_
->GetViewAtModelIndex(index
));
120 AppListItemView
* GetItemViewForPoint(const gfx::Point
& point
) {
121 for (size_t i
= 0; i
< model_
->top_level_item_list()->item_count(); ++i
) {
122 AppListItemView
* view
= GetItemViewAt(i
);
123 if (view
->bounds().Contains(point
))
129 gfx::Rect
GetItemTileRectAt(int row
, int col
) {
130 DCHECK_GT(model_
->top_level_item_list()->item_count(), 0u);
132 gfx::Insets
insets(apps_grid_view_
->GetInsets());
133 gfx::Rect
rect(gfx::Point(insets
.left(), insets
.top()),
134 AppsGridView::GetTotalTileSize());
135 rect
.Offset(col
* rect
.width(), row
* rect
.height());
139 PaginationModel
* GetPaginationModel() {
140 return apps_grid_view_
->pagination_model();
143 // Points are in |apps_grid_view_|'s coordinates.
144 AppListItemView
* SimulateDrag(AppsGridView::Pointer pointer
,
145 const gfx::Point
& from
,
146 const gfx::Point
& to
) {
147 AppListItemView
* view
= GetItemViewForPoint(from
);
150 gfx::Point translated_from
= gfx::PointAtOffsetFromOrigin(
151 from
- view
->bounds().origin());
152 gfx::Point translated_to
= gfx::PointAtOffsetFromOrigin(
153 to
- view
->bounds().origin());
155 ui::MouseEvent
pressed_event(ui::ET_MOUSE_PRESSED
,
156 translated_from
, from
, 0, 0);
157 apps_grid_view_
->InitiateDrag(view
, pointer
, pressed_event
);
159 ui::MouseEvent
drag_event(ui::ET_MOUSE_DRAGGED
,
160 translated_to
, to
, 0, 0);
161 apps_grid_view_
->UpdateDragFromItem(pointer
, drag_event
);
165 void SimulateKeyPress(ui::KeyboardCode key_code
) {
166 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
, key_code
, ui::EF_NONE
);
167 apps_grid_view_
->OnKeyPressed(key_event
);
170 scoped_ptr
<AppListTestModel
> model_
;
171 scoped_ptr
<AppsGridView
> apps_grid_view_
;
172 scoped_ptr
<AppsGridViewTestApi
> test_api_
;
175 DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest
);
178 class TestAppsGridViewFolderDelegate
: public AppsGridViewFolderDelegate
{
180 TestAppsGridViewFolderDelegate() : show_bubble_(false) {}
181 ~TestAppsGridViewFolderDelegate() override
{}
183 // Overridden from AppsGridViewFolderDelegate:
184 void UpdateFolderViewBackground(bool show_bubble
) override
{
185 show_bubble_
= show_bubble
;
188 void ReparentItem(AppListItemView
* original_drag_view
,
189 const gfx::Point
& drag_point_in_folder_grid
,
190 bool has_native_drag
) override
{}
192 void DispatchDragEventForReparent(
193 AppsGridView::Pointer pointer
,
194 const gfx::Point
& drag_point_in_folder_grid
) override
{}
196 void DispatchEndDragEventForReparent(bool events_forwarded_to_drag_drop_host
,
197 bool cancel_drag
) override
{}
199 bool IsPointOutsideOfFolderBoundary(const gfx::Point
& point
) override
{
203 bool IsOEMFolder() const override
{ return false; }
205 void SetRootLevelDragViewVisible(bool visible
) override
{}
207 bool show_bubble() { return show_bubble_
; }
212 DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate
);
215 TEST_F(AppsGridViewTest
, CreatePage
) {
216 // Fully populates a page.
217 const int kPages
= 1;
218 model_
->PopulateApps(kPages
* kTilesPerPage
);
219 EXPECT_EQ(kPages
, GetPaginationModel()->total_pages());
221 // Adds one more and gets a new page created.
222 model_
->CreateAndAddItem("Extra");
223 EXPECT_EQ(kPages
+ 1, GetPaginationModel()->total_pages());
226 TEST_F(AppsGridViewTest
, EnsureHighlightedVisible
) {
227 const int kPages
= 3;
228 model_
->PopulateApps(kPages
* kTilesPerPage
);
229 EXPECT_EQ(kPages
, GetPaginationModel()->total_pages());
230 EXPECT_EQ(0, GetPaginationModel()->selected_page());
232 // Highlight first one and last one one first page and first page should be
234 model_
->HighlightItemAt(0);
235 EXPECT_EQ(0, GetPaginationModel()->selected_page());
236 model_
->HighlightItemAt(kTilesPerPage
- 1);
237 EXPECT_EQ(0, GetPaginationModel()->selected_page());
239 // Highlight first one on 2nd page and 2nd page should be selected.
240 model_
->HighlightItemAt(kTilesPerPage
+ 1);
241 EXPECT_EQ(1, GetPaginationModel()->selected_page());
243 // Highlight last one in the model and last page should be selected.
244 model_
->HighlightItemAt(model_
->top_level_item_list()->item_count() - 1);
245 EXPECT_EQ(kPages
- 1, GetPaginationModel()->selected_page());
248 TEST_F(AppsGridViewTest
, RemoveSelectedLastApp
) {
249 const int kTotalItems
= 2;
250 const int kLastItemIndex
= kTotalItems
- 1;
252 model_
->PopulateApps(kTotalItems
);
254 AppListItemView
* last_view
= GetItemViewAt(kLastItemIndex
);
255 apps_grid_view_
->SetSelectedView(last_view
);
256 model_
->DeleteItem(model_
->GetItemName(kLastItemIndex
));
258 EXPECT_FALSE(apps_grid_view_
->IsSelectedView(last_view
));
261 AppListItemView
* view
= GetItemViewAt(0);
262 apps_grid_view_
->SetSelectedView(view
);
263 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(view
));
266 TEST_F(AppsGridViewTest
, MouseDragWithFolderDisabled
) {
267 model_
->SetFoldersEnabled(false);
268 const int kTotalItems
= 4;
269 model_
->PopulateApps(kTotalItems
);
270 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
271 model_
->GetModelContent());
273 gfx::Point from
= GetItemTileRectAt(0, 0).CenterPoint();
274 gfx::Point to
= GetItemTileRectAt(0, 1).CenterPoint();
276 // Dragging changes model order.
277 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
278 apps_grid_view_
->EndDrag(false);
279 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
280 model_
->GetModelContent());
281 test_api_
->LayoutToIdealBounds();
283 // Canceling drag should keep existing order.
284 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
285 apps_grid_view_
->EndDrag(true);
286 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
287 model_
->GetModelContent());
288 test_api_
->LayoutToIdealBounds();
290 // Deleting an item keeps remaining intact.
291 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
292 model_
->DeleteItem(model_
->GetItemName(0));
293 apps_grid_view_
->EndDrag(false);
294 EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
295 model_
->GetModelContent());
296 test_api_
->LayoutToIdealBounds();
298 // Adding a launcher item cancels the drag and respects the order.
299 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
300 EXPECT_TRUE(apps_grid_view_
->has_dragged_view());
301 model_
->CreateAndAddItem("Extra");
302 // No need to EndDrag explicitly - adding an item should do this.
303 EXPECT_FALSE(apps_grid_view_
->has_dragged_view());
304 // Even though cancelled, mouse move events can still arrive via the item
305 // view. Ensure that behaves sanely, and doesn't start a new drag.
306 ui::MouseEvent
drag_event(
307 ui::ET_MOUSE_DRAGGED
, gfx::Point(1, 1), gfx::Point(2, 2), 0, 0);
308 apps_grid_view_
->UpdateDragFromItem(AppsGridView::MOUSE
, drag_event
);
309 EXPECT_FALSE(apps_grid_view_
->has_dragged_view());
311 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
312 model_
->GetModelContent());
313 test_api_
->LayoutToIdealBounds();
316 TEST_F(AppsGridViewTest
, MouseDragItemIntoFolder
) {
317 EnsureFoldersEnabled();
319 size_t kTotalItems
= 3;
320 model_
->PopulateApps(kTotalItems
);
321 EXPECT_EQ(model_
->top_level_item_list()->item_count(), kTotalItems
);
322 EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_
->GetModelContent());
324 gfx::Point from
= GetItemTileRectAt(0, 1).CenterPoint();
325 gfx::Point to
= GetItemTileRectAt(0, 0).CenterPoint();
327 // Dragging item_1 over item_0 creates a folder.
328 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
329 apps_grid_view_
->EndDrag(false);
330 EXPECT_EQ(kTotalItems
- 1, model_
->top_level_item_list()->item_count());
331 EXPECT_EQ(AppListFolderItem::kItemType
,
332 model_
->top_level_item_list()->item_at(0)->GetItemType());
333 AppListFolderItem
* folder_item
= static_cast<AppListFolderItem
*>(
334 model_
->top_level_item_list()->item_at(0));
335 EXPECT_EQ(2u, folder_item
->ChildItemCount());
336 AppListItem
* item_0
= model_
->FindItem("Item 0");
337 EXPECT_TRUE(item_0
->IsInFolder());
338 EXPECT_EQ(folder_item
->id(), item_0
->folder_id());
339 AppListItem
* item_1
= model_
->FindItem("Item 1");
340 EXPECT_TRUE(item_1
->IsInFolder());
341 EXPECT_EQ(folder_item
->id(), item_1
->folder_id());
342 std::string expected_items
= folder_item
->id() + ",Item 2";
343 EXPECT_EQ(expected_items
, model_
->GetModelContent());
344 test_api_
->LayoutToIdealBounds();
346 // Dragging item_2 to the folder adds item_2 to the folder.
347 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
348 apps_grid_view_
->EndDrag(false);
350 EXPECT_EQ(kTotalItems
- 2, model_
->top_level_item_list()->item_count());
351 EXPECT_EQ(folder_item
->id(), model_
->GetModelContent());
352 EXPECT_EQ(3u, folder_item
->ChildItemCount());
353 item_0
= model_
->FindItem("Item 0");
354 EXPECT_TRUE(item_0
->IsInFolder());
355 EXPECT_EQ(folder_item
->id(), item_0
->folder_id());
356 item_1
= model_
->FindItem("Item 1");
357 EXPECT_TRUE(item_1
->IsInFolder());
358 EXPECT_EQ(folder_item
->id(), item_1
->folder_id());
359 AppListItem
* item_2
= model_
->FindItem("Item 2");
360 EXPECT_TRUE(item_2
->IsInFolder());
361 EXPECT_EQ(folder_item
->id(), item_2
->folder_id());
362 test_api_
->LayoutToIdealBounds();
365 TEST_F(AppsGridViewTest
, MouseDragMaxItemsInFolder
) {
366 EnsureFoldersEnabled();
368 // Create and add a folder with 15 items in it.
369 size_t kTotalItems
= kMaxFolderItems
- 1;
370 model_
->CreateAndPopulateFolderWithApps(kTotalItems
);
371 EXPECT_EQ(1u, model_
->top_level_item_list()->item_count());
372 EXPECT_EQ(AppListFolderItem::kItemType
,
373 model_
->top_level_item_list()->item_at(0)->GetItemType());
374 AppListFolderItem
* folder_item
= static_cast<AppListFolderItem
*>(
375 model_
->top_level_item_list()->item_at(0));
376 EXPECT_EQ(kTotalItems
, folder_item
->ChildItemCount());
378 // Create and add another 2 items.
379 model_
->PopulateAppWithId(kTotalItems
);
380 model_
->PopulateAppWithId(kTotalItems
+ 1);
381 EXPECT_EQ(3u, model_
->top_level_item_list()->item_count());
382 EXPECT_EQ(folder_item
->id(), model_
->top_level_item_list()->item_at(0)->id());
383 EXPECT_EQ(model_
->GetItemName(kMaxFolderItems
- 1),
384 model_
->top_level_item_list()->item_at(1)->id());
385 EXPECT_EQ(model_
->GetItemName(kMaxFolderItems
),
386 model_
->top_level_item_list()->item_at(2)->id());
388 gfx::Point from
= GetItemTileRectAt(0, 1).CenterPoint();
389 gfx::Point to
= GetItemTileRectAt(0, 0).CenterPoint();
391 // Dragging one item into the folder, the folder should accept the item.
392 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
393 apps_grid_view_
->EndDrag(false);
394 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
395 EXPECT_EQ(folder_item
->id(), model_
->top_level_item_list()->item_at(0)->id());
396 EXPECT_EQ(kMaxFolderItems
, folder_item
->ChildItemCount());
397 EXPECT_EQ(model_
->GetItemName(kMaxFolderItems
),
398 model_
->top_level_item_list()->item_at(1)->id());
399 test_api_
->LayoutToIdealBounds();
401 // Dragging the last item over the folder, the folder won't accept the new
403 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
404 apps_grid_view_
->EndDrag(false);
405 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
406 EXPECT_EQ(kMaxFolderItems
, folder_item
->ChildItemCount());
407 test_api_
->LayoutToIdealBounds();
410 // Check that moving items around doesn't allow a drop to happen into a full
412 TEST_F(AppsGridViewTest
, MouseDragMaxItemsInFolderWithMovement
) {
413 EnsureFoldersEnabled();
415 // Create and add a folder with 16 items in it.
416 size_t kTotalItems
= kMaxFolderItems
;
417 model_
->CreateAndPopulateFolderWithApps(kTotalItems
);
418 EXPECT_EQ(1u, model_
->top_level_item_list()->item_count());
419 EXPECT_EQ(AppListFolderItem::kItemType
,
420 model_
->top_level_item_list()->item_at(0)->GetItemType());
421 AppListFolderItem
* folder_item
= static_cast<AppListFolderItem
*>(
422 model_
->top_level_item_list()->item_at(0));
423 EXPECT_EQ(kTotalItems
, folder_item
->ChildItemCount());
425 // Create and add another item.
426 model_
->PopulateAppWithId(kTotalItems
);
427 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
428 EXPECT_EQ(folder_item
->id(), model_
->top_level_item_list()->item_at(0)->id());
429 EXPECT_EQ(model_
->GetItemName(kMaxFolderItems
),
430 model_
->top_level_item_list()->item_at(1)->id());
432 AppListItemView
* folder_view
=
433 GetItemViewForPoint(GetItemTileRectAt(0, 0).CenterPoint());
435 // Drag the new item to the left so that the grid reorders.
436 gfx::Point from
= GetItemTileRectAt(0, 1).CenterPoint();
437 gfx::Point to
= GetItemTileRectAt(0, 0).bottom_left();
438 to
.Offset(0, -1); // Get a point inside the rect.
439 AppListItemView
* dragged_view
= SimulateDrag(AppsGridView::MOUSE
, from
, to
);
440 test_api_
->LayoutToIdealBounds();
442 // The grid now looks like | blank | folder |.
443 EXPECT_EQ(NULL
, GetItemViewForPoint(GetItemTileRectAt(0, 0).CenterPoint()));
444 EXPECT_EQ(folder_view
,
445 GetItemViewForPoint(GetItemTileRectAt(0, 1).CenterPoint()));
447 // Move onto the folder and end the drag.
448 to
= GetItemTileRectAt(0, 1).CenterPoint();
449 gfx::Point translated_to
=
450 gfx::PointAtOffsetFromOrigin(to
- dragged_view
->bounds().origin());
451 ui::MouseEvent
drag_event(ui::ET_MOUSE_DRAGGED
, translated_to
, to
, 0, 0);
452 apps_grid_view_
->UpdateDragFromItem(AppsGridView::MOUSE
, drag_event
);
453 apps_grid_view_
->EndDrag(false);
455 // The item should not have moved into the folder.
456 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
457 EXPECT_EQ(kMaxFolderItems
, folder_item
->ChildItemCount());
458 test_api_
->LayoutToIdealBounds();
461 TEST_F(AppsGridViewTest
, MouseDragItemReorder
) {
462 // This test assumes Folders are enabled.
463 EnsureFoldersEnabled();
465 model_
->PopulateApps(4);
466 EXPECT_EQ(4u, model_
->top_level_item_list()->item_count());
467 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
468 model_
->GetModelContent());
470 // Dragging an item towards its neighbours should not reorder until the drag
471 // is past the folder drop point.
472 gfx::Point top_right
= GetItemTileRectAt(0, 1).CenterPoint();
473 gfx::Vector2d drag_vector
;
474 int half_tile_width
=
475 (GetItemTileRectAt(0, 1).x() - GetItemTileRectAt(0, 0).x()) / 2;
476 int tile_height
= GetItemTileRectAt(1, 0).y() - GetItemTileRectAt(0, 0).y();
478 // Drag left but stop before the folder dropping circle.
479 drag_vector
.set_x(-half_tile_width
- 4);
480 SimulateDrag(AppsGridView::MOUSE
, top_right
, top_right
+ drag_vector
);
481 apps_grid_view_
->EndDrag(false);
482 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
483 model_
->GetModelContent());
485 // Drag left, past the folder dropping circle.
486 drag_vector
.set_x(-3 * half_tile_width
+ 4);
487 SimulateDrag(AppsGridView::MOUSE
, top_right
, top_right
+ drag_vector
);
488 apps_grid_view_
->EndDrag(false);
489 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
490 model_
->GetModelContent());
492 // Drag down, between apps 2 and 3. The gap should open up, making space for
493 // app 0 in the bottom left.
494 drag_vector
.set_x(-half_tile_width
);
495 drag_vector
.set_y(tile_height
);
496 SimulateDrag(AppsGridView::MOUSE
, top_right
, top_right
+ drag_vector
);
497 apps_grid_view_
->EndDrag(false);
498 EXPECT_EQ(std::string("Item 1,Item 2,Item 0,Item 3"),
499 model_
->GetModelContent());
501 // Drag up, between apps 1 and 2. The gap should open up, making space for app
502 // 0 in the top right.
503 gfx::Point bottom_left
= GetItemTileRectAt(1, 0).CenterPoint();
504 drag_vector
.set_x(half_tile_width
);
505 drag_vector
.set_y(-tile_height
);
506 SimulateDrag(AppsGridView::MOUSE
, bottom_left
, bottom_left
+ drag_vector
);
507 apps_grid_view_
->EndDrag(false);
508 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
509 model_
->GetModelContent());
511 // Dragging down past the last app should reorder to the last position.
512 drag_vector
.set_x(half_tile_width
);
513 drag_vector
.set_y(2 * tile_height
);
514 SimulateDrag(AppsGridView::MOUSE
, top_right
, top_right
+ drag_vector
);
515 apps_grid_view_
->EndDrag(false);
516 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Item 0"),
517 model_
->GetModelContent());
520 TEST_F(AppsGridViewTest
, MouseDragFolderReorder
) {
521 EnsureFoldersEnabled();
523 size_t kTotalItems
= 2;
524 model_
->CreateAndPopulateFolderWithApps(kTotalItems
);
525 model_
->PopulateAppWithId(kTotalItems
);
526 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
527 EXPECT_EQ(AppListFolderItem::kItemType
,
528 model_
->top_level_item_list()->item_at(0)->GetItemType());
529 AppListFolderItem
* folder_item
= static_cast<AppListFolderItem
*>(
530 model_
->top_level_item_list()->item_at(0));
531 EXPECT_EQ("Item 2", model_
->top_level_item_list()->item_at(1)->id());
533 gfx::Point from
= GetItemTileRectAt(0, 0).CenterPoint();
534 gfx::Point to
= GetItemTileRectAt(0, 1).CenterPoint();
536 // Dragging folder over item_1 should leads to re-ordering these two
538 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
539 apps_grid_view_
->EndDrag(false);
540 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
541 EXPECT_EQ("Item 2", model_
->top_level_item_list()->item_at(0)->id());
542 EXPECT_EQ(folder_item
->id(), model_
->top_level_item_list()->item_at(1)->id());
543 test_api_
->LayoutToIdealBounds();
546 TEST_F(AppsGridViewTest
, MouseDragWithCancelDeleteAddItem
) {
547 size_t kTotalItems
= 4;
548 model_
->PopulateApps(kTotalItems
);
549 EXPECT_EQ(model_
->top_level_item_list()->item_count(), kTotalItems
);
550 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
551 model_
->GetModelContent());
553 gfx::Point from
= GetItemTileRectAt(0, 0).CenterPoint();
554 gfx::Point to
= GetItemTileRectAt(0, 1).CenterPoint();
556 // Canceling drag should keep existing order.
557 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
558 apps_grid_view_
->EndDrag(true);
559 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
560 model_
->GetModelContent());
561 test_api_
->LayoutToIdealBounds();
563 // Deleting an item keeps remaining intact.
564 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
565 model_
->DeleteItem(model_
->GetItemName(2));
566 apps_grid_view_
->EndDrag(false);
567 EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_
->GetModelContent());
568 test_api_
->LayoutToIdealBounds();
570 // Adding a launcher item cancels the drag and respects the order.
571 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
572 model_
->CreateAndAddItem("Extra");
573 apps_grid_view_
->EndDrag(false);
574 EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"),
575 model_
->GetModelContent());
576 test_api_
->LayoutToIdealBounds();
579 TEST_F(AppsGridViewTest
, MouseDragFlipPage
) {
580 test_api_
->SetPageFlipDelay(10);
581 GetPaginationModel()->SetTransitionDurations(10, 10);
583 PageFlipWaiter
page_flip_waiter(message_loop(), GetPaginationModel());
585 const int kPages
= 3;
586 model_
->PopulateApps(kPages
* kTilesPerPage
);
587 EXPECT_EQ(kPages
, GetPaginationModel()->total_pages());
588 EXPECT_EQ(0, GetPaginationModel()->selected_page());
590 gfx::Point from
= GetItemTileRectAt(0, 0).CenterPoint();
591 gfx::Point to
= gfx::Point(apps_grid_view_
->width(),
592 apps_grid_view_
->height() / 2);
594 // Drag to right edge.
595 page_flip_waiter
.Reset();
596 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
598 // Page should be flipped after sometime to hit page 1 and 2 then stop.
599 while (test_api_
->HasPendingPageFlip()) {
600 page_flip_waiter
.Wait();
602 EXPECT_EQ("1,2", page_flip_waiter
.selected_pages());
603 EXPECT_EQ(2, GetPaginationModel()->selected_page());
605 apps_grid_view_
->EndDrag(true);
607 // Now drag to the left edge and test the other direction.
610 page_flip_waiter
.Reset();
611 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
613 while (test_api_
->HasPendingPageFlip()) {
614 page_flip_waiter
.Wait();
616 EXPECT_EQ("1,0", page_flip_waiter
.selected_pages());
617 EXPECT_EQ(0, GetPaginationModel()->selected_page());
619 apps_grid_view_
->EndDrag(true);
622 TEST_F(AppsGridViewTest
, SimultaneousDragWithFolderDisabled
) {
623 model_
->SetFoldersEnabled(false);
624 const int kTotalItems
= 4;
625 model_
->PopulateApps(kTotalItems
);
626 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
627 model_
->GetModelContent());
629 gfx::Point mouse_from
= GetItemTileRectAt(0, 0).CenterPoint();
630 gfx::Point mouse_to
= GetItemTileRectAt(0, 1).CenterPoint();
632 gfx::Point touch_from
= GetItemTileRectAt(1, 0).CenterPoint();
633 gfx::Point touch_to
= GetItemTileRectAt(1, 1).CenterPoint();
635 // Starts a mouse drag first then a touch drag.
636 SimulateDrag(AppsGridView::MOUSE
, mouse_from
, mouse_to
);
637 SimulateDrag(AppsGridView::TOUCH
, touch_from
, touch_to
);
638 // Finishes the drag and mouse drag wins.
639 apps_grid_view_
->EndDrag(false);
640 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
641 model_
->GetModelContent());
642 test_api_
->LayoutToIdealBounds();
644 // Starts a touch drag first then a mouse drag.
645 SimulateDrag(AppsGridView::TOUCH
, touch_from
, touch_to
);
646 SimulateDrag(AppsGridView::MOUSE
, mouse_from
, mouse_to
);
647 // Finishes the drag and touch drag wins.
648 apps_grid_view_
->EndDrag(false);
649 EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
650 model_
->GetModelContent());
651 test_api_
->LayoutToIdealBounds();
654 TEST_F(AppsGridViewTest
, UpdateFolderBackgroundOnCancelDrag
) {
655 EnsureFoldersEnabled();
657 const int kTotalItems
= 4;
658 TestAppsGridViewFolderDelegate folder_delegate
;
659 apps_grid_view_
->set_folder_delegate(&folder_delegate
);
660 model_
->PopulateApps(kTotalItems
);
661 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
662 model_
->GetModelContent());
664 gfx::Point mouse_from
= GetItemTileRectAt(0, 0).CenterPoint();
665 gfx::Point mouse_to
= GetItemTileRectAt(0, 1).CenterPoint();
667 // Starts a mouse drag and then cancels it.
668 SimulateDrag(AppsGridView::MOUSE
, mouse_from
, mouse_to
);
669 EXPECT_TRUE(folder_delegate
.show_bubble());
670 apps_grid_view_
->EndDrag(true);
671 EXPECT_FALSE(folder_delegate
.show_bubble());
672 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
673 model_
->GetModelContent());
676 TEST_F(AppsGridViewTest
, HighlightWithKeyboard
) {
677 const int kPages
= 3;
678 const int kItems
= (kPages
- 1) * kTilesPerPage
+ 1;
679 model_
->PopulateApps(kItems
);
681 const int first_index
= 0;
682 const int last_index
= kItems
- 1;
683 const int last_index_on_page1_first_row
= kRows
- 1;
684 const int last_index_on_page1
= kTilesPerPage
- 1;
685 const int first_index_on_page2
= kTilesPerPage
;
686 const int first_index_on_page2_last_row
= 2 * kTilesPerPage
- kRows
;
687 const int last_index_on_page2_last_row
= 2 * kTilesPerPage
- 1;
689 // Try moving off the item beyond the first one.
690 apps_grid_view_
->SetSelectedView(GetItemViewAt(first_index
));
691 SimulateKeyPress(ui::VKEY_UP
);
692 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(first_index
)));
693 SimulateKeyPress(ui::VKEY_LEFT
);
694 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(first_index
)));
696 // Move to the last item and try to go past it.
697 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index
));
698 SimulateKeyPress(ui::VKEY_DOWN
);
699 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(last_index
)));
700 SimulateKeyPress(ui::VKEY_RIGHT
);
701 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(last_index
)));
703 // Move right on last item on page 1 should get to first item on page 2's last
704 // row and vice versa.
705 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index_on_page1
));
706 SimulateKeyPress(ui::VKEY_RIGHT
);
707 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
708 first_index_on_page2_last_row
)));
709 SimulateKeyPress(ui::VKEY_LEFT
);
710 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
711 last_index_on_page1
)));
713 // Up/down on page boundary does nothing.
714 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index_on_page1
));
715 SimulateKeyPress(ui::VKEY_DOWN
);
716 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
717 last_index_on_page1
)));
718 apps_grid_view_
->SetSelectedView(
719 GetItemViewAt(first_index_on_page2_last_row
));
721 SetSelectedView(GetItemViewAt(last_index_on_page1_first_row
));
722 SimulateKeyPress(ui::VKEY_UP
);
723 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
724 last_index_on_page1_first_row
)));
726 // Page up and down should go to the same item on the next and last page.
727 apps_grid_view_
->SetSelectedView(GetItemViewAt(first_index_on_page2
));
728 SimulateKeyPress(ui::VKEY_PRIOR
);
729 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
731 SimulateKeyPress(ui::VKEY_NEXT
);
732 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
733 first_index_on_page2
)));
735 // Moving onto a a page with too few apps to support the expected index snaps
736 // to the last available index.
737 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row
));
738 SimulateKeyPress(ui::VKEY_RIGHT
);
739 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
741 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row
));
742 SimulateKeyPress(ui::VKEY_NEXT
);
743 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
748 // After page switch, arrow keys select first item on current page.
749 apps_grid_view_
->SetSelectedView(GetItemViewAt(first_index
));
750 GetPaginationModel()->SelectPage(1, false);
751 SimulateKeyPress(ui::VKEY_UP
);
752 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
753 first_index_on_page2
)));
756 TEST_F(AppsGridViewTest
, ItemLabelShortNameOverride
) {
757 // If the app's full name and short name differ, the title label's tooltip
758 // should always be the full name of the app.
759 std::string
expected_text("xyz");
760 std::string
expected_tooltip("tooltip");
761 AppListItem
* item
= model_
->CreateAndAddItem("Item with short name");
762 model_
->SetItemNameAndShortName(item
, expected_tooltip
, expected_text
);
764 base::string16 actual_tooltip
;
765 AppListItemView
* item_view
= GetItemViewAt(0);
766 ASSERT_TRUE(item_view
);
767 const views::Label
* title_label
= item_view
->title();
768 EXPECT_TRUE(title_label
->GetTooltipText(
769 title_label
->bounds().CenterPoint(), &actual_tooltip
));
770 EXPECT_EQ(expected_tooltip
, base::UTF16ToUTF8(actual_tooltip
));
771 EXPECT_EQ(expected_text
, base::UTF16ToUTF8(title_label
->text()));
774 TEST_F(AppsGridViewTest
, ItemLabelNoShortName
) {
775 // If the app's full name and short name are the same, use the default tooltip
776 // behavior of the label (only show a tooltip if the title is truncated).
777 std::string
title("a");
778 AppListItem
* item
= model_
->CreateAndAddItem(title
);
779 model_
->SetItemNameAndShortName(item
, title
, "");
781 base::string16 actual_tooltip
;
782 AppListItemView
* item_view
= GetItemViewAt(0);
783 ASSERT_TRUE(item_view
);
784 const views::Label
* title_label
= item_view
->title();
785 EXPECT_FALSE(title_label
->GetTooltipText(
786 title_label
->bounds().CenterPoint(), &actual_tooltip
));
787 EXPECT_EQ(title
, base::UTF16ToUTF8(title_label
->text()));
791 } // namespace app_list