1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "ui/app_list/views/apps_grid_view.h" 6 7 #include <string> 8 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" 28 29 namespace app_list { 30 namespace test { 31 32 namespace { 33 34 const int kIconDimension = 48; 35 const int kCols = 2; 36 const int kRows = 2; 37 const int kTilesPerPage = kCols * kRows; 38 39 const int kWidth = 320; 40 const int kHeight = 240; 41 42 class PageFlipWaiter : public PaginationModelObserver { 43 public: 44 PageFlipWaiter(base::MessageLoopForUI* ui_loop, PaginationModel* model) 45 : ui_loop_(ui_loop), model_(model), wait_(false) { 46 model_->AddObserver(this); 47 } 48 49 virtual ~PageFlipWaiter() { 50 model_->RemoveObserver(this); 51 } 52 53 void Wait() { 54 DCHECK(!wait_); 55 wait_ = true; 56 57 ui_loop_->Run(); 58 wait_ = false; 59 } 60 61 void Reset() { selected_pages_.clear(); } 62 63 const std::string& selected_pages() const { return selected_pages_; } 64 65 private: 66 // PaginationModelObserver overrides: 67 virtual void TotalPagesChanged() OVERRIDE { 68 } 69 virtual void SelectedPageChanged(int old_selected, 70 int new_selected) OVERRIDE { 71 if (!selected_pages_.empty()) 72 selected_pages_ += ','; 73 selected_pages_ += base::IntToString(new_selected); 74 75 if (wait_) 76 ui_loop_->Quit(); 77 } 78 virtual void TransitionStarted() OVERRIDE { 79 } 80 virtual void TransitionChanged() OVERRIDE { 81 } 82 83 base::MessageLoopForUI* ui_loop_; 84 PaginationModel* model_; 85 bool wait_; 86 std::string selected_pages_; 87 88 DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter); 89 }; 90 91 } // namespace 92 93 class AppsGridViewTest : public views::ViewsTestBase { 94 public: 95 AppsGridViewTest() {} 96 virtual ~AppsGridViewTest() {} 97 98 // testing::Test overrides: 99 virtual void SetUp() OVERRIDE { 100 views::ViewsTestBase::SetUp(); 101 model_.reset(new AppListTestModel); 102 model_->SetFoldersEnabled(true); 103 104 apps_grid_view_.reset(new AppsGridView(NULL)); 105 apps_grid_view_->SetLayout(kIconDimension, kCols, kRows); 106 apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight))); 107 apps_grid_view_->SetModel(model_.get()); 108 apps_grid_view_->SetItemList(model_->top_level_item_list()); 109 110 test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get())); 111 } 112 virtual void TearDown() OVERRIDE { 113 apps_grid_view_.reset(); // Release apps grid view before models. 114 views::ViewsTestBase::TearDown(); 115 } 116 117 protected: 118 void EnsureFoldersEnabled() { 119 // Folders require AppList sync to be enabled. 120 CommandLine::ForCurrentProcess()->AppendSwitch( 121 switches::kEnableSyncAppList); 122 } 123 124 AppListItemView* GetItemViewAt(int index) { 125 return static_cast<AppListItemView*>( 126 test_api_->GetViewAtModelIndex(index)); 127 } 128 129 AppListItemView* GetItemViewForPoint(const gfx::Point& point) { 130 for (size_t i = 0; i < model_->top_level_item_list()->item_count(); ++i) { 131 AppListItemView* view = GetItemViewAt(i); 132 if (view->bounds().Contains(point)) 133 return view; 134 } 135 return NULL; 136 } 137 138 gfx::Rect GetItemTileRectAt(int row, int col) { 139 DCHECK_GT(model_->top_level_item_list()->item_count(), 0u); 140 141 gfx::Insets insets(apps_grid_view_->GetInsets()); 142 gfx::Rect rect(gfx::Point(insets.left(), insets.top()), 143 GetItemViewAt(0)->bounds().size()); 144 rect.Offset(col * rect.width(), row * rect.height()); 145 return rect; 146 } 147 148 PaginationModel* GetPaginationModel() { 149 return apps_grid_view_->pagination_model(); 150 } 151 152 // Points are in |apps_grid_view_|'s coordinates. 153 void SimulateDrag(AppsGridView::Pointer pointer, 154 const gfx::Point& from, 155 const gfx::Point& to) { 156 AppListItemView* view = GetItemViewForPoint(from); 157 DCHECK(view); 158 159 gfx::Point translated_from = gfx::PointAtOffsetFromOrigin( 160 from - view->bounds().origin()); 161 gfx::Point translated_to = gfx::PointAtOffsetFromOrigin( 162 to - view->bounds().origin()); 163 164 ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, 165 translated_from, from, 0, 0); 166 apps_grid_view_->InitiateDrag(view, pointer, pressed_event); 167 168 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, 169 translated_to, to, 0, 0); 170 apps_grid_view_->UpdateDragFromItem(pointer, drag_event); 171 } 172 173 void SimulateKeyPress(ui::KeyboardCode key_code) { 174 ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, 0, false); 175 apps_grid_view_->OnKeyPressed(key_event); 176 } 177 178 scoped_ptr<AppListTestModel> model_; 179 scoped_ptr<AppsGridView> apps_grid_view_; 180 scoped_ptr<AppsGridViewTestApi> test_api_; 181 182 private: 183 DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest); 184 }; 185 186 class TestAppsGridViewFolderDelegate : public AppsGridViewFolderDelegate { 187 public: 188 TestAppsGridViewFolderDelegate() : show_bubble_(false) {} 189 virtual ~TestAppsGridViewFolderDelegate() {} 190 191 // Overridden from AppsGridViewFolderDelegate: 192 virtual void UpdateFolderViewBackground(bool show_bubble) OVERRIDE { 193 show_bubble_ = show_bubble; 194 } 195 196 virtual void ReparentItem(AppListItemView* original_drag_view, 197 const gfx::Point& drag_point_in_folder_grid) 198 OVERRIDE {} 199 200 virtual void DispatchDragEventForReparent( 201 AppsGridView::Pointer pointer, 202 const gfx::Point& drag_point_in_folder_grid) OVERRIDE {} 203 204 virtual void DispatchEndDragEventForReparent( 205 bool events_forwarded_to_drag_drop_host, 206 bool cancel_drag) OVERRIDE {} 207 208 virtual bool IsPointOutsideOfFolderBoundary(const gfx::Point& point) 209 OVERRIDE { 210 return false; 211 } 212 213 virtual bool IsOEMFolder() const OVERRIDE { return false; } 214 215 virtual void SetRootLevelDragViewVisible(bool visible) OVERRIDE {} 216 217 bool show_bubble() { return show_bubble_; } 218 219 private: 220 bool show_bubble_; 221 222 DISALLOW_COPY_AND_ASSIGN(TestAppsGridViewFolderDelegate); 223 }; 224 225 TEST_F(AppsGridViewTest, CreatePage) { 226 // Fully populates a page. 227 const int kPages = 1; 228 model_->PopulateApps(kPages * kTilesPerPage); 229 EXPECT_EQ(kPages, GetPaginationModel()->total_pages()); 230 231 // Adds one more and gets a new page created. 232 model_->CreateAndAddItem("Extra"); 233 EXPECT_EQ(kPages + 1, GetPaginationModel()->total_pages()); 234 } 235 236 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) { 237 const int kPages = 3; 238 model_->PopulateApps(kPages * kTilesPerPage); 239 EXPECT_EQ(kPages, GetPaginationModel()->total_pages()); 240 EXPECT_EQ(0, GetPaginationModel()->selected_page()); 241 242 // Highlight first one and last one one first page and first page should be 243 // selected. 244 model_->HighlightItemAt(0); 245 EXPECT_EQ(0, GetPaginationModel()->selected_page()); 246 model_->HighlightItemAt(kTilesPerPage - 1); 247 EXPECT_EQ(0, GetPaginationModel()->selected_page()); 248 249 // Highlight first one on 2nd page and 2nd page should be selected. 250 model_->HighlightItemAt(kTilesPerPage + 1); 251 EXPECT_EQ(1, GetPaginationModel()->selected_page()); 252 253 // Highlight last one in the model and last page should be selected. 254 model_->HighlightItemAt(model_->top_level_item_list()->item_count() - 1); 255 EXPECT_EQ(kPages - 1, GetPaginationModel()->selected_page()); 256 } 257 258 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) { 259 const int kTotalItems = 2; 260 const int kLastItemIndex = kTotalItems - 1; 261 262 model_->PopulateApps(kTotalItems); 263 264 AppListItemView* last_view = GetItemViewAt(kLastItemIndex); 265 apps_grid_view_->SetSelectedView(last_view); 266 model_->DeleteItem(model_->GetItemName(kLastItemIndex)); 267 268 EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view)); 269 270 // No crash happens. 271 AppListItemView* view = GetItemViewAt(0); 272 apps_grid_view_->SetSelectedView(view); 273 EXPECT_TRUE(apps_grid_view_->IsSelectedView(view)); 274 } 275 276 TEST_F(AppsGridViewTest, MouseDragWithFolderDisabled) { 277 model_->SetFoldersEnabled(false); 278 const int kTotalItems = 4; 279 model_->PopulateApps(kTotalItems); 280 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), 281 model_->GetModelContent()); 282 283 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint(); 284 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint(); 285 286 // Dragging changes model order. 287 SimulateDrag(AppsGridView::MOUSE, from, to); 288 apps_grid_view_->EndDrag(false); 289 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), 290 model_->GetModelContent()); 291 test_api_->LayoutToIdealBounds(); 292 293 // Canceling drag should keep existing order. 294 SimulateDrag(AppsGridView::MOUSE, from, to); 295 apps_grid_view_->EndDrag(true); 296 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), 297 model_->GetModelContent()); 298 test_api_->LayoutToIdealBounds(); 299 300 // Deleting an item keeps remaining intact. 301 SimulateDrag(AppsGridView::MOUSE, from, to); 302 model_->DeleteItem(model_->GetItemName(0)); 303 apps_grid_view_->EndDrag(false); 304 EXPECT_EQ(std::string("Item 1,Item 2,Item 3"), 305 model_->GetModelContent()); 306 test_api_->LayoutToIdealBounds(); 307 308 // Adding a launcher item cancels the drag and respects the order. 309 SimulateDrag(AppsGridView::MOUSE, from, to); 310 model_->CreateAndAddItem("Extra"); 311 apps_grid_view_->EndDrag(false); 312 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"), 313 model_->GetModelContent()); 314 test_api_->LayoutToIdealBounds(); 315 } 316 317 TEST_F(AppsGridViewTest, MouseDragItemIntoFolder) { 318 EnsureFoldersEnabled(); 319 320 size_t kTotalItems = 3; 321 model_->PopulateApps(kTotalItems); 322 EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems); 323 EXPECT_EQ(std::string("Item 0,Item 1,Item 2"), model_->GetModelContent()); 324 325 gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint(); 326 gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint(); 327 328 // Dragging item_1 over item_0 creates a folder. 329 SimulateDrag(AppsGridView::MOUSE, from, to); 330 apps_grid_view_->EndDrag(false); 331 EXPECT_EQ(kTotalItems - 1, model_->top_level_item_list()->item_count()); 332 EXPECT_EQ(AppListFolderItem::kItemType, 333 model_->top_level_item_list()->item_at(0)->GetItemType()); 334 AppListFolderItem* folder_item = static_cast<AppListFolderItem*>( 335 model_->top_level_item_list()->item_at(0)); 336 EXPECT_EQ(2u, folder_item->ChildItemCount()); 337 AppListItem* item_0 = model_->FindItem("Item 0"); 338 EXPECT_TRUE(item_0->IsInFolder()); 339 EXPECT_EQ(folder_item->id(), item_0->folder_id()); 340 AppListItem* item_1 = model_->FindItem("Item 1"); 341 EXPECT_TRUE(item_1->IsInFolder()); 342 EXPECT_EQ(folder_item->id(), item_1->folder_id()); 343 std::string expected_items = folder_item->id() + ",Item 2"; 344 EXPECT_EQ(expected_items, model_->GetModelContent()); 345 test_api_->LayoutToIdealBounds(); 346 347 // Dragging item_2 to the folder adds item_2 to the folder. 348 SimulateDrag(AppsGridView::MOUSE, from, to); 349 apps_grid_view_->EndDrag(false); 350 351 EXPECT_EQ(kTotalItems - 2, model_->top_level_item_list()->item_count()); 352 EXPECT_EQ(folder_item->id(), model_->GetModelContent()); 353 EXPECT_EQ(3u, folder_item->ChildItemCount()); 354 item_0 = model_->FindItem("Item 0"); 355 EXPECT_TRUE(item_0->IsInFolder()); 356 EXPECT_EQ(folder_item->id(), item_0->folder_id()); 357 item_1 = model_->FindItem("Item 1"); 358 EXPECT_TRUE(item_1->IsInFolder()); 359 EXPECT_EQ(folder_item->id(), item_1->folder_id()); 360 AppListItem* item_2 = model_->FindItem("Item 2"); 361 EXPECT_TRUE(item_2->IsInFolder()); 362 EXPECT_EQ(folder_item->id(), item_2->folder_id()); 363 test_api_->LayoutToIdealBounds(); 364 } 365 366 TEST_F(AppsGridViewTest, MouseDragMaxItemsInFolder) { 367 EnsureFoldersEnabled(); 368 369 // Create and add a folder with 15 items in it. 370 size_t kTotalItems = kMaxFolderItems - 1; 371 model_->CreateAndPopulateFolderWithApps(kTotalItems); 372 EXPECT_EQ(1u, model_->top_level_item_list()->item_count()); 373 EXPECT_EQ(AppListFolderItem::kItemType, 374 model_->top_level_item_list()->item_at(0)->GetItemType()); 375 AppListFolderItem* folder_item = static_cast<AppListFolderItem*>( 376 model_->top_level_item_list()->item_at(0)); 377 EXPECT_EQ(kTotalItems, folder_item->ChildItemCount()); 378 379 // Create and add another 2 items. 380 model_->PopulateAppWithId(kTotalItems); 381 model_->PopulateAppWithId(kTotalItems + 1); 382 EXPECT_EQ(3u, model_->top_level_item_list()->item_count()); 383 EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id()); 384 EXPECT_EQ(model_->GetItemName(kMaxFolderItems - 1), 385 model_->top_level_item_list()->item_at(1)->id()); 386 EXPECT_EQ(model_->GetItemName(kMaxFolderItems), 387 model_->top_level_item_list()->item_at(2)->id()); 388 389 gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint(); 390 gfx::Point to = GetItemTileRectAt(0, 0).CenterPoint(); 391 392 // Dragging one item into the folder, the folder should accept the item. 393 SimulateDrag(AppsGridView::MOUSE, from, to); 394 apps_grid_view_->EndDrag(false); 395 EXPECT_EQ(2u, model_->top_level_item_list()->item_count()); 396 EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(0)->id()); 397 EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount()); 398 EXPECT_EQ(model_->GetItemName(kMaxFolderItems), 399 model_->top_level_item_list()->item_at(1)->id()); 400 test_api_->LayoutToIdealBounds(); 401 402 // Dragging the last item over the folder, the folder won't accept the new 403 // item, instead, it will re-order the items. 404 SimulateDrag(AppsGridView::MOUSE, from, to); 405 apps_grid_view_->EndDrag(false); 406 EXPECT_EQ(2u, model_->top_level_item_list()->item_count()); 407 EXPECT_EQ(model_->GetItemName(kMaxFolderItems), 408 model_->top_level_item_list()->item_at(0)->id()); 409 EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id()); 410 EXPECT_EQ(kMaxFolderItems, folder_item->ChildItemCount()); 411 test_api_->LayoutToIdealBounds(); 412 } 413 414 TEST_F(AppsGridViewTest, MouseDragItemReorder) { 415 // This test assumes Folders are enabled. 416 EnsureFoldersEnabled(); 417 418 size_t kTotalItems = 2; 419 model_->PopulateApps(kTotalItems); 420 EXPECT_EQ(2u, model_->top_level_item_list()->item_count()); 421 EXPECT_EQ(std::string("Item 0,Item 1"), model_->GetModelContent()); 422 423 gfx::Point from = GetItemTileRectAt(0, 1).CenterPoint(); 424 int reorder_offset = (GetItemTileRectAt(0, 1).CenterPoint() - 425 GetItemTileRectAt(0, 0).CenterPoint()).Length() - 426 kReorderDroppingCircleRadius - 427 kPreferredIconDimension / 2 + 5; 428 gfx::Point to = gfx::Point(from.x() - reorder_offset, from.y()); 429 430 // Dragging item_1 closing to item_0 should leads to re-ordering these two 431 // items. 432 SimulateDrag(AppsGridView::MOUSE, from, to); 433 apps_grid_view_->EndDrag(false); 434 EXPECT_EQ(2u, model_->top_level_item_list()->item_count()); 435 EXPECT_EQ(std::string("Item 1,Item 0"), model_->GetModelContent()); 436 test_api_->LayoutToIdealBounds(); 437 } 438 439 TEST_F(AppsGridViewTest, MouseDragFolderReorder) { 440 EnsureFoldersEnabled(); 441 442 size_t kTotalItems = 2; 443 model_->CreateAndPopulateFolderWithApps(kTotalItems); 444 model_->PopulateAppWithId(kTotalItems); 445 EXPECT_EQ(2u, model_->top_level_item_list()->item_count()); 446 EXPECT_EQ(AppListFolderItem::kItemType, 447 model_->top_level_item_list()->item_at(0)->GetItemType()); 448 AppListFolderItem* folder_item = static_cast<AppListFolderItem*>( 449 model_->top_level_item_list()->item_at(0)); 450 EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(1)->id()); 451 452 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint(); 453 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint(); 454 455 // Dragging folder over item_1 should leads to re-ordering these two 456 // items. 457 SimulateDrag(AppsGridView::MOUSE, from, to); 458 apps_grid_view_->EndDrag(false); 459 EXPECT_EQ(2u, model_->top_level_item_list()->item_count()); 460 EXPECT_EQ("Item 2", model_->top_level_item_list()->item_at(0)->id()); 461 EXPECT_EQ(folder_item->id(), model_->top_level_item_list()->item_at(1)->id()); 462 test_api_->LayoutToIdealBounds(); 463 } 464 465 TEST_F(AppsGridViewTest, MouseDragWithCancelDeleteAddItem) { 466 size_t kTotalItems = 4; 467 model_->PopulateApps(kTotalItems); 468 EXPECT_EQ(model_->top_level_item_list()->item_count(), kTotalItems); 469 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), 470 model_->GetModelContent()); 471 472 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint(); 473 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint(); 474 475 // Canceling drag should keep existing order. 476 SimulateDrag(AppsGridView::MOUSE, from, to); 477 apps_grid_view_->EndDrag(true); 478 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), 479 model_->GetModelContent()); 480 test_api_->LayoutToIdealBounds(); 481 482 // Deleting an item keeps remaining intact. 483 SimulateDrag(AppsGridView::MOUSE, from, to); 484 model_->DeleteItem(model_->GetItemName(2)); 485 apps_grid_view_->EndDrag(false); 486 EXPECT_EQ(std::string("Item 0,Item 1,Item 3"), model_->GetModelContent()); 487 test_api_->LayoutToIdealBounds(); 488 489 // Adding a launcher item cancels the drag and respects the order. 490 SimulateDrag(AppsGridView::MOUSE, from, to); 491 model_->CreateAndAddItem("Extra"); 492 apps_grid_view_->EndDrag(false); 493 EXPECT_EQ(std::string("Item 0,Item 1,Item 3,Extra"), 494 model_->GetModelContent()); 495 test_api_->LayoutToIdealBounds(); 496 } 497 498 TEST_F(AppsGridViewTest, MouseDragFlipPage) { 499 test_api_->SetPageFlipDelay(10); 500 GetPaginationModel()->SetTransitionDurations(10, 10); 501 502 PageFlipWaiter page_flip_waiter(message_loop(), GetPaginationModel()); 503 504 const int kPages = 3; 505 model_->PopulateApps(kPages * kTilesPerPage); 506 EXPECT_EQ(kPages, GetPaginationModel()->total_pages()); 507 EXPECT_EQ(0, GetPaginationModel()->selected_page()); 508 509 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint(); 510 gfx::Point to = gfx::Point(apps_grid_view_->width(), 511 apps_grid_view_->height() / 2); 512 513 // Drag to right edge. 514 page_flip_waiter.Reset(); 515 SimulateDrag(AppsGridView::MOUSE, from, to); 516 517 // Page should be flipped after sometime to hit page 1 and 2 then stop. 518 while (test_api_->HasPendingPageFlip()) { 519 page_flip_waiter.Wait(); 520 } 521 EXPECT_EQ("1,2", page_flip_waiter.selected_pages()); 522 EXPECT_EQ(2, GetPaginationModel()->selected_page()); 523 524 apps_grid_view_->EndDrag(true); 525 526 // Now drag to the left edge and test the other direction. 527 to.set_x(0); 528 529 page_flip_waiter.Reset(); 530 SimulateDrag(AppsGridView::MOUSE, from, to); 531 532 while (test_api_->HasPendingPageFlip()) { 533 page_flip_waiter.Wait(); 534 } 535 EXPECT_EQ("1,0", page_flip_waiter.selected_pages()); 536 EXPECT_EQ(0, GetPaginationModel()->selected_page()); 537 538 apps_grid_view_->EndDrag(true); 539 } 540 541 TEST_F(AppsGridViewTest, SimultaneousDragWithFolderDisabled) { 542 model_->SetFoldersEnabled(false); 543 const int kTotalItems = 4; 544 model_->PopulateApps(kTotalItems); 545 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), 546 model_->GetModelContent()); 547 548 gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint(); 549 gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint(); 550 551 gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint(); 552 gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint(); 553 554 // Starts a mouse drag first then a touch drag. 555 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); 556 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to); 557 // Finishes the drag and mouse drag wins. 558 apps_grid_view_->EndDrag(false); 559 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), 560 model_->GetModelContent()); 561 test_api_->LayoutToIdealBounds(); 562 563 // Starts a touch drag first then a mouse drag. 564 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to); 565 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); 566 // Finishes the drag and touch drag wins. 567 apps_grid_view_->EndDrag(false); 568 EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"), 569 model_->GetModelContent()); 570 test_api_->LayoutToIdealBounds(); 571 } 572 573 TEST_F(AppsGridViewTest, UpdateFolderBackgroundOnCancelDrag) { 574 EnsureFoldersEnabled(); 575 576 const int kTotalItems = 4; 577 TestAppsGridViewFolderDelegate folder_delegate; 578 apps_grid_view_->set_folder_delegate(&folder_delegate); 579 model_->PopulateApps(kTotalItems); 580 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), 581 model_->GetModelContent()); 582 583 gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint(); 584 gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint(); 585 586 // Starts a mouse drag and then cancels it. 587 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); 588 EXPECT_TRUE(folder_delegate.show_bubble()); 589 apps_grid_view_->EndDrag(true); 590 EXPECT_FALSE(folder_delegate.show_bubble()); 591 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), 592 model_->GetModelContent()); 593 } 594 595 TEST_F(AppsGridViewTest, HighlightWithKeyboard) { 596 const int kPages = 3; 597 const int kItems = (kPages - 1) * kTilesPerPage + 1; 598 model_->PopulateApps(kItems); 599 600 const int first_index = 0; 601 const int last_index = kItems - 1; 602 const int last_index_on_page1_first_row = kRows - 1; 603 const int last_index_on_page1 = kTilesPerPage - 1; 604 const int first_index_on_page2 = kTilesPerPage; 605 const int first_index_on_page2_last_row = 2 * kTilesPerPage - kRows; 606 const int last_index_on_page2_last_row = 2 * kTilesPerPage - 1; 607 608 // Try moving off the item beyond the first one. 609 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index)); 610 SimulateKeyPress(ui::VKEY_UP); 611 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index))); 612 SimulateKeyPress(ui::VKEY_LEFT); 613 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(first_index))); 614 615 // Move to the last item and try to go past it. 616 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index)); 617 SimulateKeyPress(ui::VKEY_DOWN); 618 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index))); 619 SimulateKeyPress(ui::VKEY_RIGHT); 620 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(last_index))); 621 622 // Move right on last item on page 1 should get to first item on page 2's last 623 // row and vice versa. 624 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1)); 625 SimulateKeyPress(ui::VKEY_RIGHT); 626 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 627 first_index_on_page2_last_row))); 628 SimulateKeyPress(ui::VKEY_LEFT); 629 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 630 last_index_on_page1))); 631 632 // Up/down on page boundary does nothing. 633 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page1)); 634 SimulateKeyPress(ui::VKEY_DOWN); 635 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 636 last_index_on_page1))); 637 apps_grid_view_->SetSelectedView( 638 GetItemViewAt(first_index_on_page2_last_row)); 639 apps_grid_view_-> 640 SetSelectedView(GetItemViewAt(last_index_on_page1_first_row)); 641 SimulateKeyPress(ui::VKEY_UP); 642 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 643 last_index_on_page1_first_row))); 644 645 // Page up and down should go to the same item on the next and last page. 646 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index_on_page2)); 647 SimulateKeyPress(ui::VKEY_PRIOR); 648 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 649 first_index))); 650 SimulateKeyPress(ui::VKEY_NEXT); 651 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 652 first_index_on_page2))); 653 654 // Moving onto a a page with too few apps to support the expected index snaps 655 // to the last available index. 656 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row)); 657 SimulateKeyPress(ui::VKEY_RIGHT); 658 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 659 last_index))); 660 apps_grid_view_->SetSelectedView(GetItemViewAt(last_index_on_page2_last_row)); 661 SimulateKeyPress(ui::VKEY_NEXT); 662 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 663 last_index))); 664 665 666 667 // After page switch, arrow keys select first item on current page. 668 apps_grid_view_->SetSelectedView(GetItemViewAt(first_index)); 669 GetPaginationModel()->SelectPage(1, false); 670 SimulateKeyPress(ui::VKEY_UP); 671 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt( 672 first_index_on_page2))); 673 } 674 675 TEST_F(AppsGridViewTest, ItemLabelShortNameOverride) { 676 // If the app's full name and short name differ, the title label's tooltip 677 // should always be the full name of the app. 678 std::string expected_text("xyz"); 679 std::string expected_tooltip("tooltip"); 680 AppListItem* item = model_->CreateAndAddItem("Item with short name"); 681 model_->SetItemNameAndShortName(item, expected_tooltip, expected_text); 682 683 base::string16 actual_tooltip; 684 AppListItemView* item_view = GetItemViewAt(0); 685 ASSERT_TRUE(item_view); 686 const views::Label* title_label = item_view->title(); 687 EXPECT_TRUE(title_label->GetTooltipText( 688 title_label->bounds().CenterPoint(), &actual_tooltip)); 689 EXPECT_EQ(expected_tooltip, base::UTF16ToUTF8(actual_tooltip)); 690 EXPECT_EQ(expected_text, base::UTF16ToUTF8(title_label->text())); 691 } 692 693 TEST_F(AppsGridViewTest, ItemLabelNoShortName) { 694 // If the app's full name and short name are the same, use the default tooltip 695 // behavior of the label (only show a tooltip if the title is truncated). 696 std::string title("a"); 697 AppListItem* item = model_->CreateAndAddItem(title); 698 model_->SetItemNameAndShortName(item, title, ""); 699 700 base::string16 actual_tooltip; 701 AppListItemView* item_view = GetItemViewAt(0); 702 ASSERT_TRUE(item_view); 703 const views::Label* title_label = item_view->title(); 704 EXPECT_FALSE(title_label->GetTooltipText( 705 title_label->bounds().CenterPoint(), &actual_tooltip)); 706 EXPECT_EQ(title, base::UTF16ToUTF8(title_label->text())); 707 } 708 709 } // namespace test 710 } // namespace app_list 711