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/events/event_utils.h"
28 #include "ui/views/test/views_test_base.h"
37 const int kTilesPerPage
= kCols
* kRows
;
39 class PageFlipWaiter
: public PaginationModelObserver
{
41 PageFlipWaiter(base::MessageLoopForUI
* ui_loop
, PaginationModel
* model
)
42 : ui_loop_(ui_loop
), model_(model
), wait_(false) {
43 model_
->AddObserver(this);
46 ~PageFlipWaiter() override
{ model_
->RemoveObserver(this); }
56 void Reset() { selected_pages_
.clear(); }
58 const std::string
& selected_pages() const { return selected_pages_
; }
61 // PaginationModelObserver overrides:
62 void TotalPagesChanged() override
{}
63 void SelectedPageChanged(int old_selected
, int new_selected
) override
{
64 if (!selected_pages_
.empty())
65 selected_pages_
+= ',';
66 selected_pages_
+= base::IntToString(new_selected
);
71 void TransitionStarted() override
{}
72 void TransitionChanged() override
{}
74 base::MessageLoopForUI
* ui_loop_
;
75 PaginationModel
* model_
;
77 std::string selected_pages_
;
79 DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter
);
84 class AppsGridViewTest
: public views::ViewsTestBase
{
87 ~AppsGridViewTest() override
{}
89 // testing::Test overrides:
90 void SetUp() override
{
91 views::ViewsTestBase::SetUp();
92 model_
.reset(new AppListTestModel
);
93 model_
->SetFoldersEnabled(true);
95 apps_grid_view_
.reset(new AppsGridView(NULL
));
96 apps_grid_view_
->SetLayout(kCols
, kRows
);
97 apps_grid_view_
->SetBoundsRect(
98 gfx::Rect(apps_grid_view_
->GetPreferredSize()));
99 apps_grid_view_
->SetModel(model_
.get());
100 apps_grid_view_
->SetItemList(model_
->top_level_item_list());
102 test_api_
.reset(new AppsGridViewTestApi(apps_grid_view_
.get()));
104 void TearDown() override
{
105 apps_grid_view_
.reset(); // Release apps grid view before models.
106 views::ViewsTestBase::TearDown();
110 void EnsureFoldersEnabled() {
111 #if defined(OS_MACOSX)
112 // Folders require toolkit-views app list to be enabled.
113 base::CommandLine::ForCurrentProcess()->AppendSwitch(
114 switches::kEnableMacViewsAppList
);
118 AppListItemView
* GetItemViewAt(int index
) {
119 return static_cast<AppListItemView
*>(
120 test_api_
->GetViewAtModelIndex(index
));
123 AppListItemView
* GetItemViewForPoint(const gfx::Point
& point
) {
124 for (size_t i
= 0; i
< model_
->top_level_item_list()->item_count(); ++i
) {
125 AppListItemView
* view
= GetItemViewAt(i
);
126 if (view
->bounds().Contains(point
))
132 gfx::Rect
GetItemTileRectAt(int row
, int col
) {
133 DCHECK_GT(model_
->top_level_item_list()->item_count(), 0u);
135 gfx::Insets
insets(apps_grid_view_
->GetInsets());
136 gfx::Rect
rect(gfx::Point(insets
.left(), insets
.top()),
137 AppsGridView::GetTotalTileSize());
138 rect
.Offset(col
* rect
.width(), row
* rect
.height());
142 PaginationModel
* GetPaginationModel() {
143 return apps_grid_view_
->pagination_model();
146 // Points are in |apps_grid_view_|'s coordinates.
147 AppListItemView
* SimulateDrag(AppsGridView::Pointer pointer
,
148 const gfx::Point
& from
,
149 const gfx::Point
& to
) {
150 AppListItemView
* view
= GetItemViewForPoint(from
);
153 gfx::Point translated_from
= gfx::PointAtOffsetFromOrigin(
154 from
- view
->bounds().origin());
155 gfx::Point translated_to
= gfx::PointAtOffsetFromOrigin(
156 to
- view
->bounds().origin());
158 ui::MouseEvent
pressed_event(ui::ET_MOUSE_PRESSED
, translated_from
, from
,
159 ui::EventTimeForNow(), 0, 0);
160 apps_grid_view_
->InitiateDrag(view
, pointer
, pressed_event
);
162 ui::MouseEvent
drag_event(ui::ET_MOUSE_DRAGGED
, translated_to
, to
,
163 ui::EventTimeForNow(), 0, 0);
164 apps_grid_view_
->UpdateDragFromItem(pointer
, drag_event
);
168 void SimulateKeyPress(ui::KeyboardCode key_code
) {
169 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
, key_code
, ui::EF_NONE
);
170 apps_grid_view_
->OnKeyPressed(key_event
);
173 scoped_ptr
<AppListTestModel
> model_
;
174 scoped_ptr
<AppsGridView
> apps_grid_view_
;
175 scoped_ptr
<AppsGridViewTestApi
> test_api_
;
178 DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest
);
181 class TestAppsGridViewFolderDelegate
: public AppsGridViewFolderDelegate
{
183 TestAppsGridViewFolderDelegate() : show_bubble_(false) {}
184 ~TestAppsGridViewFolderDelegate() override
{}
186 // Overridden from AppsGridViewFolderDelegate:
187 void UpdateFolderViewBackground(bool show_bubble
) override
{
188 show_bubble_
= show_bubble
;
191 void ReparentItem(AppListItemView
* original_drag_view
,
192 const gfx::Point
& drag_point_in_folder_grid
,
193 bool has_native_drag
) override
{}
195 void DispatchDragEventForReparent(
196 AppsGridView::Pointer pointer
,
197 const gfx::Point
& drag_point_in_folder_grid
) override
{}
199 void DispatchEndDragEventForReparent(bool events_forwarded_to_drag_drop_host
,
200 bool cancel_drag
) override
{}
202 bool IsPointOutsideOfFolderBoundary(const gfx::Point
& point
) override
{
206 bool IsOEMFolder() const override
{ return false; }
208 void SetRootLevelDragViewVisible(bool visible
) override
{}
210 bool show_bubble() { return show_bubble_
; }
215 DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate
);
218 TEST_F(AppsGridViewTest
, CreatePage
) {
219 // Fully populates a page.
220 const int kPages
= 1;
221 model_
->PopulateApps(kPages
* kTilesPerPage
);
222 EXPECT_EQ(kPages
, GetPaginationModel()->total_pages());
224 // Adds one more and gets a new page created.
225 model_
->CreateAndAddItem("Extra");
226 EXPECT_EQ(kPages
+ 1, GetPaginationModel()->total_pages());
229 TEST_F(AppsGridViewTest
, EnsureHighlightedVisible
) {
230 const int kPages
= 3;
231 model_
->PopulateApps(kPages
* kTilesPerPage
);
232 EXPECT_EQ(kPages
, GetPaginationModel()->total_pages());
233 EXPECT_EQ(0, GetPaginationModel()->selected_page());
235 // Highlight first one and last one one first page and first page should be
237 model_
->HighlightItemAt(0);
238 EXPECT_EQ(0, GetPaginationModel()->selected_page());
239 model_
->HighlightItemAt(kTilesPerPage
- 1);
240 EXPECT_EQ(0, GetPaginationModel()->selected_page());
242 // Highlight first one on 2nd page and 2nd page should be selected.
243 model_
->HighlightItemAt(kTilesPerPage
+ 1);
244 EXPECT_EQ(1, GetPaginationModel()->selected_page());
246 // Highlight last one in the model and last page should be selected.
247 model_
->HighlightItemAt(model_
->top_level_item_list()->item_count() - 1);
248 EXPECT_EQ(kPages
- 1, GetPaginationModel()->selected_page());
251 TEST_F(AppsGridViewTest
, RemoveSelectedLastApp
) {
252 const int kTotalItems
= 2;
253 const int kLastItemIndex
= kTotalItems
- 1;
255 model_
->PopulateApps(kTotalItems
);
257 AppListItemView
* last_view
= GetItemViewAt(kLastItemIndex
);
258 apps_grid_view_
->SetSelectedView(last_view
);
259 model_
->DeleteItem(model_
->GetItemName(kLastItemIndex
));
261 EXPECT_FALSE(apps_grid_view_
->IsSelectedView(last_view
));
264 AppListItemView
* view
= GetItemViewAt(0);
265 apps_grid_view_
->SetSelectedView(view
);
266 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(view
));
269 TEST_F(AppsGridViewTest
, MouseDragWithFolderDisabled
) {
270 model_
->SetFoldersEnabled(false);
271 const int kTotalItems
= 4;
272 model_
->PopulateApps(kTotalItems
);
273 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
274 model_
->GetModelContent());
276 gfx::Point from
= GetItemTileRectAt(0, 0).CenterPoint();
277 gfx::Point to
= GetItemTileRectAt(0, 1).CenterPoint();
279 // Dragging changes model order.
280 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
281 apps_grid_view_
->EndDrag(false);
282 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
283 model_
->GetModelContent());
284 test_api_
->LayoutToIdealBounds();
286 // Canceling drag should keep existing order.
287 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
288 apps_grid_view_
->EndDrag(true);
289 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
290 model_
->GetModelContent());
291 test_api_
->LayoutToIdealBounds();
293 // Deleting an item keeps remaining intact.
294 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
295 model_
->DeleteItem(model_
->GetItemName(0));
296 apps_grid_view_
->EndDrag(false);
297 EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
298 model_
->GetModelContent());
299 test_api_
->LayoutToIdealBounds();
301 // Adding a launcher item cancels the drag and respects the order.
302 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
303 EXPECT_TRUE(apps_grid_view_
->has_dragged_view());
304 model_
->CreateAndAddItem("Extra");
305 // No need to EndDrag explicitly - adding an item should do this.
306 EXPECT_FALSE(apps_grid_view_
->has_dragged_view());
307 // Even though cancelled, mouse move events can still arrive via the item
308 // view. Ensure that behaves sanely, and doesn't start a new drag.
309 ui::MouseEvent
drag_event(ui::ET_MOUSE_DRAGGED
, gfx::Point(1, 1),
310 gfx::Point(2, 2), ui::EventTimeForNow(), 0, 0);
311 apps_grid_view_
->UpdateDragFromItem(AppsGridView::MOUSE
, drag_event
);
312 EXPECT_FALSE(apps_grid_view_
->has_dragged_view());
314 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
315 model_
->GetModelContent());
316 test_api_
->LayoutToIdealBounds();
319 TEST_F(AppsGridViewTest
, MouseDragItemIntoFolder
) {
320 EnsureFoldersEnabled();
322 size_t kTotalItems
= 3;
323 model_
->PopulateApps(kTotalItems
);
324 EXPECT_EQ(model_
->top_level_item_list()->item_count(), kTotalItems
);
325 EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_
->GetModelContent());
327 gfx::Point from
= GetItemTileRectAt(0, 1).CenterPoint();
328 gfx::Point to
= GetItemTileRectAt(0, 0).CenterPoint();
330 // Dragging item_1 over item_0 creates a folder.
331 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
332 apps_grid_view_
->EndDrag(false);
333 EXPECT_EQ(kTotalItems
- 1, model_
->top_level_item_list()->item_count());
334 EXPECT_EQ(AppListFolderItem::kItemType
,
335 model_
->top_level_item_list()->item_at(0)->GetItemType());
336 AppListFolderItem
* folder_item
= static_cast<AppListFolderItem
*>(
337 model_
->top_level_item_list()->item_at(0));
338 EXPECT_EQ(2u, folder_item
->ChildItemCount());
339 AppListItem
* item_0
= model_
->FindItem("Item 0");
340 EXPECT_TRUE(item_0
->IsInFolder());
341 EXPECT_EQ(folder_item
->id(), item_0
->folder_id());
342 AppListItem
* item_1
= model_
->FindItem("Item 1");
343 EXPECT_TRUE(item_1
->IsInFolder());
344 EXPECT_EQ(folder_item
->id(), item_1
->folder_id());
345 std::string expected_items
= folder_item
->id() + ",Item 2";
346 EXPECT_EQ(expected_items
, model_
->GetModelContent());
347 test_api_
->LayoutToIdealBounds();
349 // Dragging item_2 to the folder adds item_2 to the folder.
350 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
351 apps_grid_view_
->EndDrag(false);
353 EXPECT_EQ(kTotalItems
- 2, model_
->top_level_item_list()->item_count());
354 EXPECT_EQ(folder_item
->id(), model_
->GetModelContent());
355 EXPECT_EQ(3u, folder_item
->ChildItemCount());
356 item_0
= model_
->FindItem("Item 0");
357 EXPECT_TRUE(item_0
->IsInFolder());
358 EXPECT_EQ(folder_item
->id(), item_0
->folder_id());
359 item_1
= model_
->FindItem("Item 1");
360 EXPECT_TRUE(item_1
->IsInFolder());
361 EXPECT_EQ(folder_item
->id(), item_1
->folder_id());
362 AppListItem
* item_2
= model_
->FindItem("Item 2");
363 EXPECT_TRUE(item_2
->IsInFolder());
364 EXPECT_EQ(folder_item
->id(), item_2
->folder_id());
365 test_api_
->LayoutToIdealBounds();
368 TEST_F(AppsGridViewTest
, MouseDragMaxItemsInFolder
) {
369 EnsureFoldersEnabled();
371 // Create and add a folder with 15 items in it.
372 size_t kTotalItems
= kMaxFolderItems
- 1;
373 model_
->CreateAndPopulateFolderWithApps(kTotalItems
);
374 EXPECT_EQ(1u, model_
->top_level_item_list()->item_count());
375 EXPECT_EQ(AppListFolderItem::kItemType
,
376 model_
->top_level_item_list()->item_at(0)->GetItemType());
377 AppListFolderItem
* folder_item
= static_cast<AppListFolderItem
*>(
378 model_
->top_level_item_list()->item_at(0));
379 EXPECT_EQ(kTotalItems
, folder_item
->ChildItemCount());
381 // Create and add another 2 items.
382 model_
->PopulateAppWithId(kTotalItems
);
383 model_
->PopulateAppWithId(kTotalItems
+ 1);
384 EXPECT_EQ(3u, model_
->top_level_item_list()->item_count());
385 EXPECT_EQ(folder_item
->id(), model_
->top_level_item_list()->item_at(0)->id());
386 EXPECT_EQ(model_
->GetItemName(kMaxFolderItems
- 1),
387 model_
->top_level_item_list()->item_at(1)->id());
388 EXPECT_EQ(model_
->GetItemName(kMaxFolderItems
),
389 model_
->top_level_item_list()->item_at(2)->id());
391 gfx::Point from
= GetItemTileRectAt(0, 1).CenterPoint();
392 gfx::Point to
= GetItemTileRectAt(0, 0).CenterPoint();
394 // Dragging one item into the folder, the folder should accept the item.
395 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
396 apps_grid_view_
->EndDrag(false);
397 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
398 EXPECT_EQ(folder_item
->id(), model_
->top_level_item_list()->item_at(0)->id());
399 EXPECT_EQ(kMaxFolderItems
, folder_item
->ChildItemCount());
400 EXPECT_EQ(model_
->GetItemName(kMaxFolderItems
),
401 model_
->top_level_item_list()->item_at(1)->id());
402 test_api_
->LayoutToIdealBounds();
404 // Dragging the last item over the folder, the folder won't accept the new
406 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
407 apps_grid_view_
->EndDrag(false);
408 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
409 EXPECT_EQ(kMaxFolderItems
, folder_item
->ChildItemCount());
410 test_api_
->LayoutToIdealBounds();
413 // Check that moving items around doesn't allow a drop to happen into a full
415 TEST_F(AppsGridViewTest
, MouseDragMaxItemsInFolderWithMovement
) {
416 EnsureFoldersEnabled();
418 // Create and add a folder with 16 items in it.
419 size_t kTotalItems
= kMaxFolderItems
;
420 model_
->CreateAndPopulateFolderWithApps(kTotalItems
);
421 EXPECT_EQ(1u, model_
->top_level_item_list()->item_count());
422 EXPECT_EQ(AppListFolderItem::kItemType
,
423 model_
->top_level_item_list()->item_at(0)->GetItemType());
424 AppListFolderItem
* folder_item
= static_cast<AppListFolderItem
*>(
425 model_
->top_level_item_list()->item_at(0));
426 EXPECT_EQ(kTotalItems
, folder_item
->ChildItemCount());
428 // Create and add another item.
429 model_
->PopulateAppWithId(kTotalItems
);
430 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
431 EXPECT_EQ(folder_item
->id(), model_
->top_level_item_list()->item_at(0)->id());
432 EXPECT_EQ(model_
->GetItemName(kMaxFolderItems
),
433 model_
->top_level_item_list()->item_at(1)->id());
435 AppListItemView
* folder_view
=
436 GetItemViewForPoint(GetItemTileRectAt(0, 0).CenterPoint());
438 // Drag the new item to the left so that the grid reorders.
439 gfx::Point from
= GetItemTileRectAt(0, 1).CenterPoint();
440 gfx::Point to
= GetItemTileRectAt(0, 0).bottom_left();
441 to
.Offset(0, -1); // Get a point inside the rect.
442 AppListItemView
* dragged_view
= SimulateDrag(AppsGridView::MOUSE
, from
, to
);
443 test_api_
->LayoutToIdealBounds();
445 // The grid now looks like | blank | folder |.
446 EXPECT_EQ(NULL
, GetItemViewForPoint(GetItemTileRectAt(0, 0).CenterPoint()));
447 EXPECT_EQ(folder_view
,
448 GetItemViewForPoint(GetItemTileRectAt(0, 1).CenterPoint()));
450 // Move onto the folder and end the drag.
451 to
= GetItemTileRectAt(0, 1).CenterPoint();
452 gfx::Point translated_to
=
453 gfx::PointAtOffsetFromOrigin(to
- dragged_view
->bounds().origin());
454 ui::MouseEvent
drag_event(ui::ET_MOUSE_DRAGGED
, translated_to
, to
,
455 ui::EventTimeForNow(), 0, 0);
456 apps_grid_view_
->UpdateDragFromItem(AppsGridView::MOUSE
, drag_event
);
457 apps_grid_view_
->EndDrag(false);
459 // The item should not have moved into the folder.
460 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
461 EXPECT_EQ(kMaxFolderItems
, folder_item
->ChildItemCount());
462 test_api_
->LayoutToIdealBounds();
465 TEST_F(AppsGridViewTest
, MouseDragItemReorder
) {
466 // This test assumes Folders are enabled.
467 EnsureFoldersEnabled();
469 model_
->PopulateApps(4);
470 EXPECT_EQ(4u, model_
->top_level_item_list()->item_count());
471 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
472 model_
->GetModelContent());
474 // Dragging an item towards its neighbours should not reorder until the drag
475 // is past the folder drop point.
476 gfx::Point top_right
= GetItemTileRectAt(0, 1).CenterPoint();
477 gfx::Vector2d drag_vector
;
478 int half_tile_width
=
479 (GetItemTileRectAt(0, 1).x() - GetItemTileRectAt(0, 0).x()) / 2;
480 int tile_height
= GetItemTileRectAt(1, 0).y() - GetItemTileRectAt(0, 0).y();
482 // Drag left but stop before the folder dropping circle.
483 drag_vector
.set_x(-half_tile_width
- 4);
484 SimulateDrag(AppsGridView::MOUSE
, top_right
, top_right
+ drag_vector
);
485 apps_grid_view_
->EndDrag(false);
486 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
487 model_
->GetModelContent());
489 // Drag left, past the folder dropping circle.
490 drag_vector
.set_x(-3 * half_tile_width
+ 4);
491 SimulateDrag(AppsGridView::MOUSE
, top_right
, top_right
+ drag_vector
);
492 apps_grid_view_
->EndDrag(false);
493 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
494 model_
->GetModelContent());
496 // Drag down, between apps 2 and 3. The gap should open up, making space for
497 // app 0 in the bottom left.
498 drag_vector
.set_x(-half_tile_width
);
499 drag_vector
.set_y(tile_height
);
500 SimulateDrag(AppsGridView::MOUSE
, top_right
, top_right
+ drag_vector
);
501 apps_grid_view_
->EndDrag(false);
502 EXPECT_EQ(std::string("Item 1,Item 2,Item 0,Item 3"),
503 model_
->GetModelContent());
505 // Drag up, between apps 1 and 2. The gap should open up, making space for app
506 // 0 in the top right.
507 gfx::Point bottom_left
= GetItemTileRectAt(1, 0).CenterPoint();
508 drag_vector
.set_x(half_tile_width
);
509 drag_vector
.set_y(-tile_height
);
510 SimulateDrag(AppsGridView::MOUSE
, bottom_left
, bottom_left
+ drag_vector
);
511 apps_grid_view_
->EndDrag(false);
512 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
513 model_
->GetModelContent());
515 // Dragging down past the last app should reorder to the last position.
516 drag_vector
.set_x(half_tile_width
);
517 drag_vector
.set_y(2 * tile_height
);
518 SimulateDrag(AppsGridView::MOUSE
, top_right
, top_right
+ drag_vector
);
519 apps_grid_view_
->EndDrag(false);
520 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Item 0"),
521 model_
->GetModelContent());
524 TEST_F(AppsGridViewTest
, MouseDragFolderReorder
) {
525 EnsureFoldersEnabled();
527 size_t kTotalItems
= 2;
528 model_
->CreateAndPopulateFolderWithApps(kTotalItems
);
529 model_
->PopulateAppWithId(kTotalItems
);
530 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
531 EXPECT_EQ(AppListFolderItem::kItemType
,
532 model_
->top_level_item_list()->item_at(0)->GetItemType());
533 AppListFolderItem
* folder_item
= static_cast<AppListFolderItem
*>(
534 model_
->top_level_item_list()->item_at(0));
535 EXPECT_EQ("Item 2", model_
->top_level_item_list()->item_at(1)->id());
537 gfx::Point from
= GetItemTileRectAt(0, 0).CenterPoint();
538 gfx::Point to
= GetItemTileRectAt(0, 1).CenterPoint();
540 // Dragging folder over item_1 should leads to re-ordering these two
542 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
543 apps_grid_view_
->EndDrag(false);
544 EXPECT_EQ(2u, model_
->top_level_item_list()->item_count());
545 EXPECT_EQ("Item 2", model_
->top_level_item_list()->item_at(0)->id());
546 EXPECT_EQ(folder_item
->id(), model_
->top_level_item_list()->item_at(1)->id());
547 test_api_
->LayoutToIdealBounds();
550 TEST_F(AppsGridViewTest
, MouseDragWithCancelDeleteAddItem
) {
551 size_t kTotalItems
= 4;
552 model_
->PopulateApps(kTotalItems
);
553 EXPECT_EQ(model_
->top_level_item_list()->item_count(), kTotalItems
);
554 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
555 model_
->GetModelContent());
557 gfx::Point from
= GetItemTileRectAt(0, 0).CenterPoint();
558 gfx::Point to
= GetItemTileRectAt(0, 1).CenterPoint();
560 // Canceling drag should keep existing order.
561 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
562 apps_grid_view_
->EndDrag(true);
563 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
564 model_
->GetModelContent());
565 test_api_
->LayoutToIdealBounds();
567 // Deleting an item keeps remaining intact.
568 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
569 model_
->DeleteItem(model_
->GetItemName(2));
570 apps_grid_view_
->EndDrag(false);
571 EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_
->GetModelContent());
572 test_api_
->LayoutToIdealBounds();
574 // Adding a launcher item cancels the drag and respects the order.
575 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
576 model_
->CreateAndAddItem("Extra");
577 apps_grid_view_
->EndDrag(false);
578 EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"),
579 model_
->GetModelContent());
580 test_api_
->LayoutToIdealBounds();
583 TEST_F(AppsGridViewTest
, MouseDragFlipPage
) {
584 test_api_
->SetPageFlipDelay(10);
585 GetPaginationModel()->SetTransitionDurations(10, 10);
587 PageFlipWaiter
page_flip_waiter(message_loop(), GetPaginationModel());
589 const int kPages
= 3;
590 model_
->PopulateApps(kPages
* kTilesPerPage
);
591 EXPECT_EQ(kPages
, GetPaginationModel()->total_pages());
592 EXPECT_EQ(0, GetPaginationModel()->selected_page());
594 gfx::Point from
= GetItemTileRectAt(0, 0).CenterPoint();
595 gfx::Point to
= gfx::Point(apps_grid_view_
->width(),
596 apps_grid_view_
->height() / 2);
598 // Drag to right edge.
599 page_flip_waiter
.Reset();
600 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
602 // Page should be flipped after sometime to hit page 1 and 2 then stop.
603 while (test_api_
->HasPendingPageFlip()) {
604 page_flip_waiter
.Wait();
606 EXPECT_EQ("1,2", page_flip_waiter
.selected_pages());
607 EXPECT_EQ(2, GetPaginationModel()->selected_page());
609 apps_grid_view_
->EndDrag(true);
611 // Now drag to the left edge and test the other direction.
614 page_flip_waiter
.Reset();
615 SimulateDrag(AppsGridView::MOUSE
, from
, to
);
617 while (test_api_
->HasPendingPageFlip()) {
618 page_flip_waiter
.Wait();
620 EXPECT_EQ("1,0", page_flip_waiter
.selected_pages());
621 EXPECT_EQ(0, GetPaginationModel()->selected_page());
623 apps_grid_view_
->EndDrag(true);
626 TEST_F(AppsGridViewTest
, SimultaneousDragWithFolderDisabled
) {
627 model_
->SetFoldersEnabled(false);
628 const int kTotalItems
= 4;
629 model_
->PopulateApps(kTotalItems
);
630 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
631 model_
->GetModelContent());
633 gfx::Point mouse_from
= GetItemTileRectAt(0, 0).CenterPoint();
634 gfx::Point mouse_to
= GetItemTileRectAt(0, 1).CenterPoint();
636 gfx::Point touch_from
= GetItemTileRectAt(1, 0).CenterPoint();
637 gfx::Point touch_to
= GetItemTileRectAt(1, 1).CenterPoint();
639 // Starts a mouse drag first then a touch drag.
640 SimulateDrag(AppsGridView::MOUSE
, mouse_from
, mouse_to
);
641 SimulateDrag(AppsGridView::TOUCH
, touch_from
, touch_to
);
642 // Finishes the drag and mouse drag wins.
643 apps_grid_view_
->EndDrag(false);
644 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
645 model_
->GetModelContent());
646 test_api_
->LayoutToIdealBounds();
648 // Starts a touch drag first then a mouse drag.
649 SimulateDrag(AppsGridView::TOUCH
, touch_from
, touch_to
);
650 SimulateDrag(AppsGridView::MOUSE
, mouse_from
, mouse_to
);
651 // Finishes the drag and touch drag wins.
652 apps_grid_view_
->EndDrag(false);
653 EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
654 model_
->GetModelContent());
655 test_api_
->LayoutToIdealBounds();
658 TEST_F(AppsGridViewTest
, UpdateFolderBackgroundOnCancelDrag
) {
659 EnsureFoldersEnabled();
661 const int kTotalItems
= 4;
662 TestAppsGridViewFolderDelegate folder_delegate
;
663 apps_grid_view_
->set_folder_delegate(&folder_delegate
);
664 model_
->PopulateApps(kTotalItems
);
665 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
666 model_
->GetModelContent());
668 gfx::Point mouse_from
= GetItemTileRectAt(0, 0).CenterPoint();
669 gfx::Point mouse_to
= GetItemTileRectAt(0, 1).CenterPoint();
671 // Starts a mouse drag and then cancels it.
672 SimulateDrag(AppsGridView::MOUSE
, mouse_from
, mouse_to
);
673 EXPECT_TRUE(folder_delegate
.show_bubble());
674 apps_grid_view_
->EndDrag(true);
675 EXPECT_FALSE(folder_delegate
.show_bubble());
676 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
677 model_
->GetModelContent());
680 TEST_F(AppsGridViewTest
, HighlightWithKeyboard
) {
681 const int kPages
= 3;
682 const int kItems
= (kPages
- 1) * kTilesPerPage
+ 1;
683 model_
->PopulateApps(kItems
);
685 const int first_index
= 0;
686 const int last_index
= kItems
- 1;
687 const int last_index_on_page1_first_row
= kRows
- 1;
688 const int last_index_on_page1
= kTilesPerPage
- 1;
689 const int first_index_on_page2
= kTilesPerPage
;
690 const int first_index_on_page2_last_row
= 2 * kTilesPerPage
- kRows
;
691 const int last_index_on_page2_last_row
= 2 * kTilesPerPage
- 1;
693 // Try moving off the item beyond the first one.
694 apps_grid_view_
->SetSelectedView(GetItemViewAt(first_index
));
695 SimulateKeyPress(ui::VKEY_UP
);
696 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(first_index
)));
697 SimulateKeyPress(ui::VKEY_LEFT
);
698 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(first_index
)));
700 // Move to the last item and try to go past it.
701 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index
));
702 SimulateKeyPress(ui::VKEY_DOWN
);
703 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(last_index
)));
704 SimulateKeyPress(ui::VKEY_RIGHT
);
705 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(last_index
)));
707 // Move right on last item on page 1 should get to first item on page 2's last
708 // row and vice versa.
709 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index_on_page1
));
710 SimulateKeyPress(ui::VKEY_RIGHT
);
711 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
712 first_index_on_page2_last_row
)));
713 SimulateKeyPress(ui::VKEY_LEFT
);
714 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
715 last_index_on_page1
)));
717 // Up/down on page boundary does nothing.
718 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index_on_page1
));
719 SimulateKeyPress(ui::VKEY_DOWN
);
720 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
721 last_index_on_page1
)));
722 apps_grid_view_
->SetSelectedView(
723 GetItemViewAt(first_index_on_page2_last_row
));
725 SetSelectedView(GetItemViewAt(last_index_on_page1_first_row
));
726 SimulateKeyPress(ui::VKEY_UP
);
727 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
728 last_index_on_page1_first_row
)));
730 // Page up and down should go to the same item on the next and last page.
731 apps_grid_view_
->SetSelectedView(GetItemViewAt(first_index_on_page2
));
732 SimulateKeyPress(ui::VKEY_PRIOR
);
733 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
735 SimulateKeyPress(ui::VKEY_NEXT
);
736 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
737 first_index_on_page2
)));
739 // Moving onto a page with too few apps to support the expected index snaps
740 // to the last available index.
741 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row
));
742 SimulateKeyPress(ui::VKEY_RIGHT
);
743 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
745 apps_grid_view_
->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row
));
746 SimulateKeyPress(ui::VKEY_NEXT
);
747 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
750 // After page switch, arrow keys select first item on current page.
751 apps_grid_view_
->SetSelectedView(GetItemViewAt(first_index
));
752 GetPaginationModel()->SelectPage(1, false);
753 SimulateKeyPress(ui::VKEY_LEFT
);
754 EXPECT_TRUE(apps_grid_view_
->IsSelectedView(GetItemViewAt(
755 first_index_on_page2
)));
758 TEST_F(AppsGridViewTest
, ItemLabelShortNameOverride
) {
759 // If the app's full name and short name differ, the title label's tooltip
760 // should always be the full name of the app.
761 std::string
expected_text("xyz");
762 std::string
expected_tooltip("tooltip");
763 AppListItem
* item
= model_
->CreateAndAddItem("Item with short name");
764 model_
->SetItemNameAndShortName(item
, expected_tooltip
, expected_text
);
766 base::string16 actual_tooltip
;
767 AppListItemView
* item_view
= GetItemViewAt(0);
768 ASSERT_TRUE(item_view
);
769 const views::Label
* title_label
= item_view
->title();
770 EXPECT_TRUE(item_view
->GetTooltipText(title_label
->bounds().CenterPoint(),
772 EXPECT_EQ(expected_tooltip
, base::UTF16ToUTF8(actual_tooltip
));
773 EXPECT_EQ(expected_text
, base::UTF16ToUTF8(title_label
->text()));
776 TEST_F(AppsGridViewTest
, ItemLabelNoShortName
) {
777 // If the app's full name and short name are the same, use the default tooltip
778 // behavior of the label (only show a tooltip if the title is truncated).
779 std::string
title("a");
780 AppListItem
* item
= model_
->CreateAndAddItem(title
);
781 model_
->SetItemNameAndShortName(item
, title
, "");
783 base::string16 actual_tooltip
;
784 AppListItemView
* item_view
= GetItemViewAt(0);
785 ASSERT_TRUE(item_view
);
786 const views::Label
* title_label
= item_view
->title();
787 EXPECT_FALSE(title_label
->GetTooltipText(
788 title_label
->bounds().CenterPoint(), &actual_tooltip
));
789 EXPECT_EQ(title
, base::UTF16ToUTF8(title_label
->text()));
793 } // namespace app_list