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 const int kWidth = 320;
39 const int kHeight = 240;
41 class PageFlipWaiter : public PaginationModelObserver {
43 PageFlipWaiter(base::MessageLoopForUI* ui_loop, PaginationModel* model)
44 : ui_loop_(ui_loop), model_(model), wait_(false) {
45 model_->AddObserver(this);
48 virtual ~PageFlipWaiter() {
49 model_->RemoveObserver(this);
60 void Reset() { selected_pages_.clear(); }
62 const std::string& selected_pages() const { return selected_pages_; }
65 // PaginationModelObserver overrides:
66 virtual void TotalPagesChanged() OVERRIDE {
68 virtual void SelectedPageChanged(int old_selected,
69 int new_selected) OVERRIDE {
70 if (!selected_pages_.empty())
71 selected_pages_ += ',';
72 selected_pages_ += base::IntToString(new_selected);
77 virtual void TransitionStarted() OVERRIDE {
79 virtual void TransitionChanged() OVERRIDE {
82 base::MessageLoopForUI* ui_loop_;
83 PaginationModel* model_;
85 std::string selected_pages_;
87 DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter);
92 class AppsGridViewTest : public views::ViewsTestBase {
95 virtual ~AppsGridViewTest() {}
97 // testing::Test overrides:
98 virtual void SetUp() OVERRIDE {
99 views::ViewsTestBase::SetUp();
100 model_.reset(new AppListTestModel);
101 model_->SetFoldersEnabled(true);
103 apps_grid_view_.reset(new AppsGridView(NULL));
104 apps_grid_view_->SetLayout(kCols, kRows);
105 apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight)));
106 apps_grid_view_->SetModel(model_.get());
107 apps_grid_view_->SetItemList(model_->top_level_item_list());
109 test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get()));
111 virtual void TearDown() OVERRIDE {
112 apps_grid_view_.reset(); // Release apps grid view before models.
113 views::ViewsTestBase::TearDown();
117 void EnsureFoldersEnabled() {
118 // Folders require AppList sync to be enabled.
119 CommandLine::ForCurrentProcess()->AppendSwitch(
120 switches::kEnableSyncAppList);
123 AppListItemView* GetItemViewAt(int index) {
124 return static_cast<AppListItemView*>(
125 test_api_->GetViewAtModelIndex(index));
128 AppListItemView* GetItemViewForPoint(const gfx::Point& point) {
129 for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i) {
130 AppListItemView* view = GetItemViewAt(i);
131 if (view->bounds().Contains(point))
137 gfx::Rect GetItemTileRectAt(int row, int col) {
138 DCHECK_GT(model_->top_level_item_list()->item_count(), 0u);
140 gfx::Insets insets(apps_grid_view_->GetInsets());
141 gfx::Rect rect(gfx::Point(insets.left(), insets.top()),
142 GetItemViewAt(0)->bounds().size());
143 rect.Offset(col * rect.width(), row * rect.height());
147 PaginationModel* GetPaginationModel() {
148 return apps_grid_view_->pagination_model();
151 // Points are in |apps_grid_view_|'s coordinates.
152 void SimulateDrag(AppsGridView::Pointer pointer,
153 const gfx::Point& from,
154 const gfx::Point& to) {
155 AppListItemView* view = GetItemViewForPoint(from);
158 gfx::Point translated_from = gfx::PointAtOffsetFromOrigin(
159 from - view->bounds().origin());
160 gfx::Point translated_to = gfx::PointAtOffsetFromOrigin(
161 to - view->bounds().origin());
163 ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED,
164 translated_from, from, 0, 0);
165 apps_grid_view_->InitiateDrag(view, pointer, pressed_event);
167 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED,
168 translated_to, to, 0, 0);
169 apps_grid_view_->UpdateDragFromItem(pointer, drag_event);
172 void SimulateKeyPress(ui::KeyboardCode key_code) {
173 ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, ui::EF_NONE);
174 apps_grid_view_->OnKeyPressed(key_event);
177 scoped_ptr<AppListTestModel> model_;
178 scoped_ptr<AppsGridView> apps_grid_view_;
179 scoped_ptr<AppsGridViewTestApi> test_api_;
182 DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest);
185 class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate {
187 TestAppsGridViewFolderDelegate() : show_bubble_(false) {}
188 virtual ~TestAppsGridViewFolderDelegate() {}
190 // Overridden from AppsGridViewFolderDelegate:
191 virtual void UpdateFolderViewBackground(bool show_bubble) OVERRIDE {
192 show_bubble_ = show_bubble;
195 virtual void ReparentItem(AppListItemView* original_drag_view,
196 const gfx::Point& drag_point_in_folder_grid)
199 virtual void DispatchDragEventForReparent(
200 AppsGridView::Pointer pointer,
201 const gfx::Point& drag_point_in_folder_grid) OVERRIDE {}
203 virtual void DispatchEndDragEventForReparent(
204 bool events_forwarded_to_drag_drop_host,
205 bool cancel_drag) OVERRIDE {}
207 virtual bool IsPointOutsideOfFolderBoundary(const gfx::Point& point)
212 virtual bool IsOEMFolder() const OVERRIDE { return false; }
214 virtual void SetRootLevelDragViewVisible(bool visible) OVERRIDE {}
216 bool show_bubble() { return show_bubble_; }
221 DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate);
224 TEST_F(AppsGridViewTest, CreatePage) {
225 // Fully populates a page.
226 const int kPages = 1;
227 model_->PopulateApps(kPages * kTilesPerPage);
228 EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
230 // Adds one more and gets a new page created.
231 model_->CreateAndAddItem("Extra");
232 EXPECT_EQ(kPages + 1, GetPaginationModel()->total_pages());
235 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) {
236 const int kPages = 3;
237 model_->PopulateApps(kPages * kTilesPerPage);
238 EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
239 EXPECT_EQ(0, GetPaginationModel()->selected_page());
241 // Highlight first one and last one one first page and first page should be
243 model_->HighlightItemAt(0);
244 EXPECT_EQ(0, GetPaginationModel()->selected_page());
245 model_->HighlightItemAt(kTilesPerPage - 1);
246 EXPECT_EQ(0, GetPaginationModel()->selected_page());
248 // Highlight first one on 2nd page and 2nd page should be selected.
249 model_->HighlightItemAt(kTilesPerPage + 1);
250 EXPECT_EQ(1, GetPaginationModel()->selected_page());
252 // Highlight last one in the model and last page should be selected.
253 model_->HighlightItemAt(model_->top_level_item_list()->item_count() - 1);
254 EXPECT_EQ(kPages - 1, GetPaginationModel()->selected_page());
257 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) {
258 const int kTotalItems = 2;
259 const int kLastItemIndex = kTotalItems - 1;
261 model_->PopulateApps(kTotalItems);
263 AppListItemView* last_view = GetItemViewAt(kLastItemIndex);
264 apps_grid_view_->SetSelectedView(last_view);
265 model_->DeleteItem(model_->GetItemName(kLastItemIndex));
267 EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view));
270 AppListItemView* view = GetItemViewAt(0);
271 apps_grid_view_->SetSelectedView(view);
272 EXPECT_TRUE(apps_grid_view_->IsSelectedView(view));
275 TEST_F(AppsGridViewTest, MouseDragWithFolderDisabled) {
276 model_->SetFoldersEnabled(false);
277 const int kTotalItems = 4;
278 model_->PopulateApps(kTotalItems);
279 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
280 model_->GetModelContent());
282 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
283 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
285 // Dragging changes model order.
286 SimulateDrag(AppsGridView::MOUSE, from, to);
287 apps_grid_view_->EndDrag(false);
288 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
289 model_->GetModelContent());
290 test_api_->LayoutToIdealBounds();
292 // Canceling drag should keep existing order.
293 SimulateDrag(AppsGridView::MOUSE, from, to);
294 apps_grid_view_->EndDrag(true);
295 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
296 model_->GetModelContent());
297 test_api_->LayoutToIdealBounds();
299 // Deleting an item keeps remaining intact.
300 SimulateDrag(AppsGridView::MOUSE, from, to);
301 model_->DeleteItem(model_->GetItemName(0));
302 apps_grid_view_->EndDrag(false);
303 EXPECT_EQ(std::string("Item 1,Item 2,Item 3"),
304 model_->GetModelContent());
305 test_api_->LayoutToIdealBounds();
307 // Adding a launcher item cancels the drag and respects the order.
308 SimulateDrag(AppsGridView::MOUSE, from, to);
309 EXPECT_TRUE(apps_grid_view_->has_dragged_view());
310 model_->CreateAndAddItem("Extra");
311 // No need to EndDrag explicitly - adding an item should do this.
312 EXPECT_FALSE(apps_grid_view_->has_dragged_view());
313 // Even though cancelled, mouse move events can still arrive via the item
314 // view. Ensure that behaves sanely, and doesn't start a new drag.
315 ui::MouseEvent drag_event(
316 ui::ET_MOUSE_DRAGGED, gfx::Point(1, 1), gfx::Point(2, 2), 0, 0);
317 apps_grid_view_->UpdateDragFromItem(AppsGridView::MOUSE, drag_event);
318 EXPECT_FALSE(apps_grid_view_->has_dragged_view());
320 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"),
321 model_->GetModelContent());
322 test_api_->LayoutToIdealBounds();
325 TEST_F(AppsGridViewTest, MouseDragItemIntoFolder) {
326 EnsureFoldersEnabled();
328 size_t kTotalItems = 3;
329 model_->PopulateApps(kTotalItems);
330 EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
331 EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_->GetModelContent());
333 gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
334 gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();
336 // Dragging item_1 over item_0 creates a folder.
337 SimulateDrag(AppsGridView::MOUSE, from, to);
338 apps_grid_view_->EndDrag(false);
339 EXPECT_EQ(kTotalItems - 1, model_->top_level_item_list()->item_count());
340 EXPECT_EQ(AppListFolderItem::kItemType,
341 model_->top_level_item_list()->item_at(0)->GetItemType());
342 AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
343 model_->top_level_item_list()->item_at(0));
344 EXPECT_EQ(2u, folder_item->ChildItemCount());
345 AppListItem* item_0 = model_->FindItem("Item 0");
346 EXPECT_TRUE(item_0->IsInFolder());
347 EXPECT_EQ(folder_item->id(), item_0->folder_id());
348 AppListItem* item_1 = model_->FindItem("Item 1");
349 EXPECT_TRUE(item_1->IsInFolder());
350 EXPECT_EQ(folder_item->id(), item_1->folder_id());
351 std::string expected_items = folder_item->id() + ",Item 2";
352 EXPECT_EQ(expected_items, model_->GetModelContent());
353 test_api_->LayoutToIdealBounds();
355 // Dragging item_2 to the folder adds item_2 to the folder.
356 SimulateDrag(AppsGridView::MOUSE, from, to);
357 apps_grid_view_->EndDrag(false);
359 EXPECT_EQ(kTotalItems - 2, model_->top_level_item_list()->item_count());
360 EXPECT_EQ(folder_item->id(), model_->GetModelContent());
361 EXPECT_EQ(3u, folder_item->ChildItemCount());
362 item_0 = model_->FindItem("Item 0");
363 EXPECT_TRUE(item_0->IsInFolder());
364 EXPECT_EQ(folder_item->id(), item_0->folder_id());
365 item_1 = model_->FindItem("Item 1");
366 EXPECT_TRUE(item_1->IsInFolder());
367 EXPECT_EQ(folder_item->id(), item_1->folder_id());
368 AppListItem* item_2 = model_->FindItem("Item 2");
369 EXPECT_TRUE(item_2->IsInFolder());
370 EXPECT_EQ(folder_item->id(), item_2->folder_id());
371 test_api_->LayoutToIdealBounds();
374 TEST_F(AppsGridViewTest, MouseDragMaxItemsInFolder) {
375 EnsureFoldersEnabled();
377 // Create and add a folder with 15 items in it.
378 size_t kTotalItems = kMaxFolderItems - 1;
379 model_->CreateAndPopulateFolderWithApps(kTotalItems);
380 EXPECT_EQ(1u, model_->top_level_item_list()->item_count());
381 EXPECT_EQ(AppListFolderItem::kItemType,
382 model_->top_level_item_list()->item_at(0)->GetItemType());
383 AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
384 model_->top_level_item_list()->item_at(0));
385 EXPECT_EQ(kTotalItems, folder_item->ChildItemCount());
387 // Create and add another 2 items.
388 model_->PopulateAppWithId(kTotalItems);
389 model_->PopulateAppWithId(kTotalItems + 1);
390 EXPECT_EQ(3u, model_->top_level_item_list()->item_count());
391 EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
392 EXPECT_EQ(model_->GetItemName(kMaxFolderItems - 1),
393 model_->top_level_item_list()->item_at(1)->id());
394 EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
395 model_->top_level_item_list()->item_at(2)->id());
397 gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint();
398 gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint();
400 // Dragging one item into the folder, the folder should accept the item.
401 SimulateDrag(AppsGridView::MOUSE, from, to);
402 apps_grid_view_->EndDrag(false);
403 EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
404 EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id());
405 EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
406 EXPECT_EQ(model_->GetItemName(kMaxFolderItems),
407 model_->top_level_item_list()->item_at(1)->id());
408 test_api_->LayoutToIdealBounds();
410 // Dragging the last item over the folder, the folder won't accept the new
412 SimulateDrag(AppsGridView::MOUSE, from, to);
413 apps_grid_view_->EndDrag(false);
414 EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
415 EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount());
416 test_api_->LayoutToIdealBounds();
419 TEST_F(AppsGridViewTest, MouseDragItemReorder) {
420 // This test assumes Folders are enabled.
421 EnsureFoldersEnabled();
423 model_->PopulateApps(4);
424 EXPECT_EQ(4u, model_->top_level_item_list()->item_count());
425 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
426 model_->GetModelContent());
428 // Dragging an item towards its neighbours should not reorder until the drag
429 // is past the folder drop point.
430 gfx::Point top_right = GetItemTileRectAt(0, 1).CenterPoint();
431 gfx::Vector2d drag_vector;
432 int half_tile_width =
433 (GetItemTileRectAt(0, 1).x() - GetItemTileRectAt(0, 0).x()) / 2;
434 int tile_height = GetItemTileRectAt(1, 0).y() - GetItemTileRectAt(0, 0).y();
436 // Drag left but stop before the folder dropping circle.
437 drag_vector.set_x(-half_tile_width - 5);
438 SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
439 apps_grid_view_->EndDrag(false);
440 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
441 model_->GetModelContent());
443 // Drag left, past the folder dropping circle.
444 drag_vector.set_x(-3 * half_tile_width + 5);
445 SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
446 apps_grid_view_->EndDrag(false);
447 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
448 model_->GetModelContent());
450 // Drag down, between apps 2 and 3. The gap should open up, making space for
451 // app 0 in the bottom left.
452 drag_vector.set_x(-half_tile_width);
453 drag_vector.set_y(tile_height);
454 SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
455 apps_grid_view_->EndDrag(false);
456 EXPECT_EQ(std::string("Item 1,Item 2,Item 0,Item 3"),
457 model_->GetModelContent());
459 // Drag up, between apps 1 and 2. The gap should open up, making space for app
460 // 0 in the top right.
461 gfx::Point bottom_left = GetItemTileRectAt(1, 0).CenterPoint();
462 drag_vector.set_x(half_tile_width);
463 drag_vector.set_y(-tile_height);
464 SimulateDrag(AppsGridView::MOUSE, bottom_left, bottom_left + drag_vector);
465 apps_grid_view_->EndDrag(false);
466 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
467 model_->GetModelContent());
469 // Dragging down past the last app should reorder to the last position.
470 drag_vector.set_x(half_tile_width);
471 drag_vector.set_y(2 * tile_height);
472 SimulateDrag(AppsGridView::MOUSE, top_right, top_right + drag_vector);
473 apps_grid_view_->EndDrag(false);
474 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Item 0"),
475 model_->GetModelContent());
478 TEST_F(AppsGridViewTest, MouseDragFolderReorder) {
479 EnsureFoldersEnabled();
481 size_t kTotalItems = 2;
482 model_->CreateAndPopulateFolderWithApps(kTotalItems);
483 model_->PopulateAppWithId(kTotalItems);
484 EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
485 EXPECT_EQ(AppListFolderItem::kItemType,
486 model_->top_level_item_list()->item_at(0)->GetItemType());
487 AppListFolderItem* folder_item = static_cast<AppListFolderItem*>(
488 model_->top_level_item_list()->item_at(0));
489 EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(1)->id());
491 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
492 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
494 // Dragging folder over item_1 should leads to re-ordering these two
496 SimulateDrag(AppsGridView::MOUSE, from, to);
497 apps_grid_view_->EndDrag(false);
498 EXPECT_EQ(2u, model_->top_level_item_list()->item_count());
499 EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(0)->id());
500 EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id());
501 test_api_->LayoutToIdealBounds();
504 TEST_F(AppsGridViewTest, MouseDragWithCancelDeleteAddItem) {
505 size_t kTotalItems = 4;
506 model_->PopulateApps(kTotalItems);
507 EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems);
508 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
509 model_->GetModelContent());
511 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
512 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint();
514 // Canceling drag should keep existing order.
515 SimulateDrag(AppsGridView::MOUSE, from, to);
516 apps_grid_view_->EndDrag(true);
517 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
518 model_->GetModelContent());
519 test_api_->LayoutToIdealBounds();
521 // Deleting an item keeps remaining intact.
522 SimulateDrag(AppsGridView::MOUSE, from, to);
523 model_->DeleteItem(model_->GetItemName(2));
524 apps_grid_view_->EndDrag(false);
525 EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_->GetModelContent());
526 test_api_->LayoutToIdealBounds();
528 // Adding a launcher item cancels the drag and respects the order.
529 SimulateDrag(AppsGridView::MOUSE, from, to);
530 model_->CreateAndAddItem("Extra");
531 apps_grid_view_->EndDrag(false);
532 EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"),
533 model_->GetModelContent());
534 test_api_->LayoutToIdealBounds();
537 TEST_F(AppsGridViewTest, MouseDragFlipPage) {
538 test_api_->SetPageFlipDelay(10);
539 GetPaginationModel()->SetTransitionDurations(10, 10);
541 PageFlipWaiter page_flip_waiter(message_loop(), GetPaginationModel());
543 const int kPages = 3;
544 model_->PopulateApps(kPages * kTilesPerPage);
545 EXPECT_EQ(kPages, GetPaginationModel()->total_pages());
546 EXPECT_EQ(0, GetPaginationModel()->selected_page());
548 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint();
549 gfx::Point to = gfx::Point(apps_grid_view_->width(),
550 apps_grid_view_->height() / 2);
552 // Drag to right edge.
553 page_flip_waiter.Reset();
554 SimulateDrag(AppsGridView::MOUSE, from, to);
556 // Page should be flipped after sometime to hit page 1 and 2 then stop.
557 while (test_api_->HasPendingPageFlip()) {
558 page_flip_waiter.Wait();
560 EXPECT_EQ("1,2", page_flip_waiter.selected_pages());
561 EXPECT_EQ(2, GetPaginationModel()->selected_page());
563 apps_grid_view_->EndDrag(true);
565 // Now drag to the left edge and test the other direction.
568 page_flip_waiter.Reset();
569 SimulateDrag(AppsGridView::MOUSE, from, to);
571 while (test_api_->HasPendingPageFlip()) {
572 page_flip_waiter.Wait();
574 EXPECT_EQ("1,0", page_flip_waiter.selected_pages());
575 EXPECT_EQ(0, GetPaginationModel()->selected_page());
577 apps_grid_view_->EndDrag(true);
580 TEST_F(AppsGridViewTest, SimultaneousDragWithFolderDisabled) {
581 model_->SetFoldersEnabled(false);
582 const int kTotalItems = 4;
583 model_->PopulateApps(kTotalItems);
584 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
585 model_->GetModelContent());
587 gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
588 gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
590 gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint();
591 gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint();
593 // Starts a mouse drag first then a touch drag.
594 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
595 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
596 // Finishes the drag and mouse drag wins.
597 apps_grid_view_->EndDrag(false);
598 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"),
599 model_->GetModelContent());
600 test_api_->LayoutToIdealBounds();
602 // Starts a touch drag first then a mouse drag.
603 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to);
604 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
605 // Finishes the drag and touch drag wins.
606 apps_grid_view_->EndDrag(false);
607 EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"),
608 model_->GetModelContent());
609 test_api_->LayoutToIdealBounds();
612 TEST_F(AppsGridViewTest, UpdateFolderBackgroundOnCancelDrag) {
613 EnsureFoldersEnabled();
615 const int kTotalItems = 4;
616 TestAppsGridViewFolderDelegate folder_delegate;
617 apps_grid_view_->set_folder_delegate(&folder_delegate);
618 model_->PopulateApps(kTotalItems);
619 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
620 model_->GetModelContent());
622 gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint();
623 gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint();
625 // Starts a mouse drag and then cancels it.
626 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to);
627 EXPECT_TRUE(folder_delegate.show_bubble());
628 apps_grid_view_->EndDrag(true);
629 EXPECT_FALSE(folder_delegate.show_bubble());
630 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"),
631 model_->GetModelContent());
634 TEST_F(AppsGridViewTest, HighlightWithKeyboard) {
635 const int kPages = 3;
636 const int kItems = (kPages - 1) * kTilesPerPage + 1;
637 model_->PopulateApps(kItems);
639 const int first_index = 0;
640 const int last_index = kItems - 1;
641 const int last_index_on_page1_first_row = kRows - 1;
642 const int last_index_on_page1 = kTilesPerPage - 1;
643 const int first_index_on_page2 = kTilesPerPage;
644 const int first_index_on_page2_last_row = 2 * kTilesPerPage - kRows;
645 const int last_index_on_page2_last_row = 2 * kTilesPerPage - 1;
647 // Try moving off the item beyond the first one.
648 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
649 SimulateKeyPress(ui::VKEY_UP);
650 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
651 SimulateKeyPress(ui::VKEY_LEFT);
652 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index)));
654 // Move to the last item and try to go past it.
655 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index));
656 SimulateKeyPress(ui::VKEY_DOWN);
657 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
658 SimulateKeyPress(ui::VKEY_RIGHT);
659 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index)));
661 // Move right on last item on page 1 should get to first item on page 2's last
662 // row and vice versa.
663 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
664 SimulateKeyPress(ui::VKEY_RIGHT);
665 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
666 first_index_on_page2_last_row)));
667 SimulateKeyPress(ui::VKEY_LEFT);
668 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
669 last_index_on_page1)));
671 // Up/down on page boundary does nothing.
672 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1));
673 SimulateKeyPress(ui::VKEY_DOWN);
674 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
675 last_index_on_page1)));
676 apps_grid_view_->SetSelectedView(
677 GetItemViewAt(first_index_on_page2_last_row));
679 SetSelectedView(GetItemViewAt(last_index_on_page1_first_row));
680 SimulateKeyPress(ui::VKEY_UP);
681 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
682 last_index_on_page1_first_row)));
684 // Page up and down should go to the same item on the next and last page.
685 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index_on_page2));
686 SimulateKeyPress(ui::VKEY_PRIOR);
687 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
689 SimulateKeyPress(ui::VKEY_NEXT);
690 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
691 first_index_on_page2)));
693 // Moving onto a a page with too few apps to support the expected index snaps
694 // to the last available index.
695 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
696 SimulateKeyPress(ui::VKEY_RIGHT);
697 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
699 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row));
700 SimulateKeyPress(ui::VKEY_NEXT);
701 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
706 // After page switch, arrow keys select first item on current page.
707 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index));
708 GetPaginationModel()->SelectPage(1, false);
709 SimulateKeyPress(ui::VKEY_UP);
710 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(
711 first_index_on_page2)));
714 TEST_F(AppsGridViewTest, ItemLabelShortNameOverride) {
715 // If the app's full name and short name differ, the title label's tooltip
716 // should always be the full name of the app.
717 std::string expected_text("xyz");
718 std::string expected_tooltip("tooltip");
719 AppListItem* item = model_->CreateAndAddItem("Item with short name");
720 model_->SetItemNameAndShortName(item, expected_tooltip, expected_text);
722 base::string16 actual_tooltip;
723 AppListItemView* item_view = GetItemViewAt(0);
724 ASSERT_TRUE(item_view);
725 const views::Label* title_label = item_view->title();
726 EXPECT_TRUE(title_label->GetTooltipText(
727 title_label->bounds().CenterPoint(), &actual_tooltip));
728 EXPECT_EQ(expected_tooltip, base::UTF16ToUTF8(actual_tooltip));
729 EXPECT_EQ(expected_text, base::UTF16ToUTF8(title_label->text()));
732 TEST_F(AppsGridViewTest, ItemLabelNoShortName) {
733 // If the app's full name and short name are the same, use the default tooltip
734 // behavior of the label (only show a tooltip if the title is truncated).
735 std::string title("a");
736 AppListItem* item = model_->CreateAndAddItem(title);
737 model_->SetItemNameAndShortName(item, title, "");
739 base::string16 actual_tooltip;
740 AppListItemView* item_view = GetItemViewAt(0);
741 ASSERT_TRUE(item_view);
742 const views::Label* title_label = item_view->title();
743 EXPECT_FALSE(title_label->GetTooltipText(
744 title_label->bounds().CenterPoint(), &actual_tooltip));
745 EXPECT_EQ(title, base::UTF16ToUTF8(title_label->text()));
749 } // namespace app_list