| OLD | NEW |
| (Empty) |
| 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/apps_grid_view.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/basictypes.h" | |
| 10 #include "base/compiler_specific.h" | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 #include "base/message_loop.h" | |
| 13 #include "base/stringprintf.h" | |
| 14 #include "base/timer.h" | |
| 15 #include "testing/gtest/include/gtest/gtest.h" | |
| 16 #include "ui/app_list/app_list_item_model.h" | |
| 17 #include "ui/app_list/app_list_item_view.h" | |
| 18 #include "ui/app_list/app_list_model.h" | |
| 19 #include "ui/app_list/pagination_model.h" | |
| 20 #include "ui/app_list/test/apps_grid_view_test_api.h" | |
| 21 | |
| 22 namespace app_list { | |
| 23 namespace test { | |
| 24 | |
| 25 namespace { | |
| 26 | |
| 27 const int kIconDimension = 48; | |
| 28 const int kCols = 2; | |
| 29 const int kRows = 2; | |
| 30 const int kTilesPerPage = kCols * kRows; | |
| 31 | |
| 32 const int kWidth = 320; | |
| 33 const int kHeight = 240; | |
| 34 | |
| 35 class PageFlipWaiter : public PaginationModelObserver { | |
| 36 public: | |
| 37 PageFlipWaiter(MessageLoopForUI* ui_loop, | |
| 38 PaginationModel* model) | |
| 39 : ui_loop_(ui_loop), | |
| 40 model_(model), | |
| 41 wait_(false), | |
| 42 page_changed_(false) { | |
| 43 model_->AddObserver(this); | |
| 44 } | |
| 45 | |
| 46 virtual ~PageFlipWaiter() { | |
| 47 model_->RemoveObserver(this); | |
| 48 } | |
| 49 | |
| 50 bool Wait(int time_out_ms) { | |
| 51 DCHECK(!wait_); | |
| 52 wait_ = true; | |
| 53 page_changed_ = false; | |
| 54 wait_timer_.Stop(); | |
| 55 wait_timer_.Start(FROM_HERE, | |
| 56 base::TimeDelta::FromMilliseconds(time_out_ms), | |
| 57 this, &PageFlipWaiter::OnWaitTimeOut); | |
| 58 ui_loop_->Run(); | |
| 59 wait_ = false; | |
| 60 return page_changed_; | |
| 61 } | |
| 62 | |
| 63 private: | |
| 64 void OnWaitTimeOut() { | |
| 65 ui_loop_->Quit(); | |
| 66 } | |
| 67 | |
| 68 // PaginationModelObserver overrides: | |
| 69 virtual void TotalPagesChanged() OVERRIDE { | |
| 70 } | |
| 71 virtual void SelectedPageChanged(int old_selected, | |
| 72 int new_selected) OVERRIDE { | |
| 73 page_changed_ = true; | |
| 74 if (wait_) | |
| 75 ui_loop_->Quit(); | |
| 76 } | |
| 77 virtual void TransitionChanged() OVERRIDE { | |
| 78 } | |
| 79 | |
| 80 MessageLoopForUI* ui_loop_; | |
| 81 PaginationModel* model_; | |
| 82 bool wait_; | |
| 83 bool page_changed_; | |
| 84 base::OneShotTimer<PageFlipWaiter> wait_timer_; | |
| 85 | |
| 86 DISALLOW_COPY_AND_ASSIGN(PageFlipWaiter); | |
| 87 }; | |
| 88 | |
| 89 } // namespace | |
| 90 | |
| 91 class AppsGridViewTest : public testing::Test { | |
| 92 public: | |
| 93 AppsGridViewTest() {} | |
| 94 virtual ~AppsGridViewTest() {} | |
| 95 | |
| 96 // testing::Test overrides: | |
| 97 virtual void SetUp() OVERRIDE { | |
| 98 model_.reset(new AppListModel); | |
| 99 pagination_model_.reset(new PaginationModel); | |
| 100 | |
| 101 apps_grid_view_.reset(new AppsGridView(NULL, pagination_model_.get())); | |
| 102 apps_grid_view_->SetLayout(kIconDimension, kCols, kRows); | |
| 103 apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight))); | |
| 104 apps_grid_view_->SetModel(model_.get()); | |
| 105 | |
| 106 test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get())); | |
| 107 } | |
| 108 virtual void TearDown() OVERRIDE { | |
| 109 apps_grid_view_.reset(); // Release apps grid view before models. | |
| 110 } | |
| 111 | |
| 112 protected: | |
| 113 void PopulateApps(int n) { | |
| 114 for (int i = 0; i < n; ++i) { | |
| 115 std::string title = base::StringPrintf("Item %d", i); | |
| 116 model_->apps()->Add(CreateItem(title)); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 // Get a string of all apps in |model| joined with ','. | |
| 121 std::string GetModelContent() { | |
| 122 std::string content; | |
| 123 for (size_t i = 0; i < model_->apps()->item_count(); ++i) { | |
| 124 if (i > 0) | |
| 125 content += ','; | |
| 126 content += model_->apps()->GetItemAt(i)->title(); | |
| 127 } | |
| 128 return content; | |
| 129 } | |
| 130 | |
| 131 AppListItemModel* CreateItem(const std::string& title) { | |
| 132 AppListItemModel* item = new AppListItemModel; | |
| 133 item->SetTitle(title); | |
| 134 return item; | |
| 135 } | |
| 136 | |
| 137 void HighlightItemAt(int index) { | |
| 138 AppListItemModel* item = model_->apps()->GetItemAt(index); | |
| 139 item->SetHighlighted(true); | |
| 140 } | |
| 141 | |
| 142 AppListItemView* GetItemViewAt(int index) { | |
| 143 return static_cast<AppListItemView*>( | |
| 144 test_api_->GetViewAtModelIndex(index)); | |
| 145 } | |
| 146 | |
| 147 AppListItemView* GetItemViewForPoint(const gfx::Point& point) { | |
| 148 for (size_t i = 0; i < model_->apps()->item_count(); ++i) { | |
| 149 AppListItemView* view = GetItemViewAt(i); | |
| 150 if (view->bounds().Contains(point)) | |
| 151 return view; | |
| 152 } | |
| 153 return NULL; | |
| 154 } | |
| 155 | |
| 156 gfx::Rect GetItemTileRectAt(int row, int col) { | |
| 157 DCHECK_GT(model_->apps()->item_count(), 0u); | |
| 158 | |
| 159 gfx::Insets insets(apps_grid_view_->GetInsets()); | |
| 160 gfx::Rect rect(gfx::Point(insets.left(), insets.top()), | |
| 161 GetItemViewAt(0)->bounds().size()); | |
| 162 rect.Offset(col * rect.width(), row * rect.height()); | |
| 163 return rect; | |
| 164 } | |
| 165 | |
| 166 // Points are in |apps_grid_view_|'s coordinates. | |
| 167 void SimulateDrag(AppsGridView::Pointer pointer, | |
| 168 const gfx::Point& from, | |
| 169 const gfx::Point& to) { | |
| 170 AppListItemView* view = GetItemViewForPoint(from); | |
| 171 DCHECK(view); | |
| 172 | |
| 173 gfx::Point translated_from = gfx::PointAtOffsetFromOrigin( | |
| 174 from - view->bounds().origin()); | |
| 175 gfx::Point translated_to = gfx::PointAtOffsetFromOrigin( | |
| 176 to - view->bounds().origin()); | |
| 177 | |
| 178 ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, | |
| 179 translated_from, from, 0); | |
| 180 apps_grid_view_->InitiateDrag(view, pointer, pressed_event); | |
| 181 | |
| 182 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, | |
| 183 translated_to, to, 0); | |
| 184 apps_grid_view_->UpdateDrag(view, pointer, drag_event); | |
| 185 } | |
| 186 | |
| 187 void SimulateKeyPress(ui::KeyboardCode key_code) { | |
| 188 ui::KeyEvent key_event(ui::ET_KEY_PRESSED, key_code, 0, false); | |
| 189 apps_grid_view_->OnKeyPressed(key_event); | |
| 190 } | |
| 191 | |
| 192 scoped_ptr<AppListModel> model_; | |
| 193 scoped_ptr<PaginationModel> pagination_model_; | |
| 194 scoped_ptr<AppsGridView> apps_grid_view_; | |
| 195 scoped_ptr<AppsGridViewTestApi> test_api_; | |
| 196 | |
| 197 MessageLoopForUI message_loop_; | |
| 198 | |
| 199 private: | |
| 200 DISALLOW_COPY_AND_ASSIGN(AppsGridViewTest); | |
| 201 }; | |
| 202 | |
| 203 TEST_F(AppsGridViewTest, CreatePage) { | |
| 204 // Fully populates a page. | |
| 205 const int kPages = 1; | |
| 206 PopulateApps(kPages * kTilesPerPage); | |
| 207 EXPECT_EQ(kPages, pagination_model_->total_pages()); | |
| 208 | |
| 209 // Adds one more and gets a new page created. | |
| 210 model_->apps()->Add(CreateItem(std::string("Extra"))); | |
| 211 EXPECT_EQ(kPages + 1, pagination_model_->total_pages()); | |
| 212 } | |
| 213 | |
| 214 TEST_F(AppsGridViewTest, EnsureHighlightedVisible) { | |
| 215 const int kPages = 3; | |
| 216 PopulateApps(kPages * kTilesPerPage); | |
| 217 EXPECT_EQ(kPages, pagination_model_->total_pages()); | |
| 218 EXPECT_EQ(0, pagination_model_->selected_page()); | |
| 219 | |
| 220 // Highlight first one and last one one first page and first page should be | |
| 221 // selected. | |
| 222 HighlightItemAt(0); | |
| 223 EXPECT_EQ(0, pagination_model_->selected_page()); | |
| 224 HighlightItemAt(kTilesPerPage - 1); | |
| 225 EXPECT_EQ(0, pagination_model_->selected_page()); | |
| 226 | |
| 227 // Highlight first one on 2nd page and 2nd page should be selected. | |
| 228 HighlightItemAt(kTilesPerPage + 1); | |
| 229 EXPECT_EQ(1, pagination_model_->selected_page()); | |
| 230 | |
| 231 // Highlight last one in the model and last page should be selected. | |
| 232 HighlightItemAt(model_->apps()->item_count() - 1); | |
| 233 EXPECT_EQ(kPages - 1, pagination_model_->selected_page()); | |
| 234 } | |
| 235 | |
| 236 TEST_F(AppsGridViewTest, RemoveSelectedLastApp) { | |
| 237 const int kTotalItems = 2; | |
| 238 const int kLastItemIndex = kTotalItems - 1; | |
| 239 | |
| 240 PopulateApps(kTotalItems); | |
| 241 | |
| 242 AppListItemView* last_view = GetItemViewAt(kLastItemIndex); | |
| 243 apps_grid_view_->SetSelectedView(last_view); | |
| 244 model_->apps()->DeleteAt(kLastItemIndex); | |
| 245 | |
| 246 EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view)); | |
| 247 | |
| 248 // No crash happens. | |
| 249 AppListItemView* view = GetItemViewAt(0); | |
| 250 apps_grid_view_->SetSelectedView(view); | |
| 251 EXPECT_TRUE(apps_grid_view_->IsSelectedView(view)); | |
| 252 } | |
| 253 | |
| 254 TEST_F(AppsGridViewTest, MouseDrag) { | |
| 255 const int kTotalItems = 4; | |
| 256 PopulateApps(kTotalItems); | |
| 257 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), | |
| 258 GetModelContent()); | |
| 259 | |
| 260 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint(); | |
| 261 gfx::Point to = GetItemTileRectAt(0, 1).CenterPoint(); | |
| 262 | |
| 263 // Dragging changes model order. | |
| 264 SimulateDrag(AppsGridView::MOUSE, from, to); | |
| 265 apps_grid_view_->EndDrag(false); | |
| 266 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), | |
| 267 GetModelContent()); | |
| 268 test_api_->LayoutToIdealBounds(); | |
| 269 | |
| 270 // Canceling drag should keep existing order. | |
| 271 SimulateDrag(AppsGridView::MOUSE, from, to); | |
| 272 apps_grid_view_->EndDrag(true); | |
| 273 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), | |
| 274 GetModelContent()); | |
| 275 test_api_->LayoutToIdealBounds(); | |
| 276 | |
| 277 // Deleting an item keeps remaining intact. | |
| 278 SimulateDrag(AppsGridView::MOUSE, from, to); | |
| 279 model_->apps()->DeleteAt(1); | |
| 280 apps_grid_view_->EndDrag(false); | |
| 281 EXPECT_EQ(std::string("Item 1,Item 2,Item 3"), | |
| 282 GetModelContent()); | |
| 283 test_api_->LayoutToIdealBounds(); | |
| 284 | |
| 285 // Adding a launcher item cancels the drag and respects the order. | |
| 286 SimulateDrag(AppsGridView::MOUSE, from, to); | |
| 287 model_->apps()->Add(CreateItem(std::string("Extra"))); | |
| 288 apps_grid_view_->EndDrag(false); | |
| 289 EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"), | |
| 290 GetModelContent()); | |
| 291 test_api_->LayoutToIdealBounds(); | |
| 292 } | |
| 293 | |
| 294 TEST_F(AppsGridViewTest, MouseDragFlipPage) { | |
| 295 test_api_->SetPageFlipDelay(10); | |
| 296 pagination_model_->SetTransitionDurations(10, 10); | |
| 297 | |
| 298 PageFlipWaiter page_flip_waiter(&message_loop_, | |
| 299 pagination_model_.get()); | |
| 300 | |
| 301 const int kPages = 3; | |
| 302 PopulateApps(kPages * kTilesPerPage); | |
| 303 EXPECT_EQ(kPages, pagination_model_->total_pages()); | |
| 304 EXPECT_EQ(0, pagination_model_->selected_page()); | |
| 305 | |
| 306 gfx::Point from = GetItemTileRectAt(0, 0).CenterPoint(); | |
| 307 gfx::Point to = gfx::Point(apps_grid_view_->width(), | |
| 308 apps_grid_view_->height() / 2); | |
| 309 | |
| 310 // Drag to right edge. | |
| 311 SimulateDrag(AppsGridView::MOUSE, from, to); | |
| 312 | |
| 313 // Page should be flipped after sometime. | |
| 314 EXPECT_TRUE(page_flip_waiter.Wait(100)); | |
| 315 EXPECT_EQ(1, pagination_model_->selected_page()); | |
| 316 | |
| 317 // Stay there and page should be flipped again. | |
| 318 EXPECT_TRUE(page_flip_waiter.Wait(100)); | |
| 319 EXPECT_EQ(2, pagination_model_->selected_page()); | |
| 320 | |
| 321 // Stay there longer and no page flip happen since we are at the last page. | |
| 322 EXPECT_FALSE(page_flip_waiter.Wait(100)); | |
| 323 EXPECT_EQ(2, pagination_model_->selected_page()); | |
| 324 | |
| 325 apps_grid_view_->EndDrag(true); | |
| 326 | |
| 327 // Now drag to the left edge and test the other direction. | |
| 328 to.set_x(0); | |
| 329 | |
| 330 SimulateDrag(AppsGridView::MOUSE, from, to); | |
| 331 | |
| 332 EXPECT_TRUE(page_flip_waiter.Wait(100)); | |
| 333 EXPECT_EQ(1, pagination_model_->selected_page()); | |
| 334 | |
| 335 EXPECT_TRUE(page_flip_waiter.Wait(100)); | |
| 336 EXPECT_EQ(0, pagination_model_->selected_page()); | |
| 337 | |
| 338 EXPECT_FALSE(page_flip_waiter.Wait(100)); | |
| 339 EXPECT_EQ(0, pagination_model_->selected_page()); | |
| 340 apps_grid_view_->EndDrag(true); | |
| 341 } | |
| 342 | |
| 343 TEST_F(AppsGridViewTest, SimultaneousDrag) { | |
| 344 const int kTotalItems = 4; | |
| 345 PopulateApps(kTotalItems); | |
| 346 EXPECT_EQ(std::string("Item 0,Item 1,Item 2,Item 3"), | |
| 347 GetModelContent()); | |
| 348 | |
| 349 gfx::Point mouse_from = GetItemTileRectAt(0, 0).CenterPoint(); | |
| 350 gfx::Point mouse_to = GetItemTileRectAt(0, 1).CenterPoint(); | |
| 351 | |
| 352 gfx::Point touch_from = GetItemTileRectAt(1, 0).CenterPoint(); | |
| 353 gfx::Point touch_to = GetItemTileRectAt(1, 1).CenterPoint(); | |
| 354 | |
| 355 // Starts a mouse drag first then a touch drag. | |
| 356 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); | |
| 357 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to); | |
| 358 // Finishes the drag and mouse drag wins. | |
| 359 apps_grid_view_->EndDrag(false); | |
| 360 EXPECT_EQ(std::string("Item 1,Item 0,Item 2,Item 3"), | |
| 361 GetModelContent()); | |
| 362 test_api_->LayoutToIdealBounds(); | |
| 363 | |
| 364 // Starts a touch drag first then a mouse drag. | |
| 365 SimulateDrag(AppsGridView::TOUCH, touch_from, touch_to); | |
| 366 SimulateDrag(AppsGridView::MOUSE, mouse_from, mouse_to); | |
| 367 // Finishes the drag and touch drag wins. | |
| 368 apps_grid_view_->EndDrag(false); | |
| 369 EXPECT_EQ(std::string("Item 1,Item 0,Item 3,Item 2"), | |
| 370 GetModelContent()); | |
| 371 test_api_->LayoutToIdealBounds(); | |
| 372 } | |
| 373 | |
| 374 TEST_F(AppsGridViewTest, HighlightWithKeyboard) { | |
| 375 const int kTotalitems = 2; | |
| 376 PopulateApps(kTotalitems); | |
| 377 apps_grid_view_->SetSelectedView(GetItemViewAt(0)); | |
| 378 | |
| 379 // Try moving off the item to an invalid location. | |
| 380 SimulateKeyPress(ui::VKEY_UP); | |
| 381 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(0))); | |
| 382 SimulateKeyPress(ui::VKEY_DOWN); | |
| 383 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(0))); | |
| 384 SimulateKeyPress(ui::VKEY_LEFT); | |
| 385 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(0))); | |
| 386 | |
| 387 // Move to the second item and try to go past it. | |
| 388 SimulateKeyPress(ui::VKEY_RIGHT); | |
| 389 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(1))); | |
| 390 SimulateKeyPress(ui::VKEY_RIGHT); | |
| 391 EXPECT_TRUE(apps_grid_view_->IsSelectedView(GetItemViewAt(1))); | |
| 392 } | |
| 393 | |
| 394 } // namespace test | |
| 395 } // namespace app_list | |
| OLD | NEW |