OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ui/app_list/views/app_list_main_view.h" | 5 #include "ui/app_list/views/app_list_main_view.h" |
6 | 6 |
| 7 #include "base/memory/scoped_ptr.h" |
| 8 #include "base/run_loop.h" |
| 9 #include "base/time/time.h" |
| 10 #include "base/timer/timer.h" |
7 #include "testing/gtest/include/gtest/gtest.h" | 11 #include "testing/gtest/include/gtest/gtest.h" |
8 #include "ui/app_list/pagination_model.h" | 12 #include "ui/app_list/pagination_model.h" |
9 #include "ui/app_list/test/app_list_test_model.h" | 13 #include "ui/app_list/test/app_list_test_model.h" |
10 #include "ui/app_list/test/app_list_test_view_delegate.h" | 14 #include "ui/app_list/test/app_list_test_view_delegate.h" |
| 15 #include "ui/app_list/views/app_list_folder_view.h" |
| 16 #include "ui/app_list/views/app_list_item_view.h" |
11 #include "ui/app_list/views/apps_container_view.h" | 17 #include "ui/app_list/views/apps_container_view.h" |
12 #include "ui/app_list/views/apps_grid_view.h" | 18 #include "ui/app_list/views/apps_grid_view.h" |
13 #include "ui/app_list/views/contents_view.h" | 19 #include "ui/app_list/views/contents_view.h" |
| 20 #include "ui/app_list/views/test/apps_grid_view_test_api.h" |
14 #include "ui/views/test/views_test_base.h" | 21 #include "ui/views/test/views_test_base.h" |
15 #include "ui/views/view_model.h" | 22 #include "ui/views/view_model.h" |
16 #include "ui/views/widget/widget.h" | 23 #include "ui/views/widget/widget.h" |
17 | 24 |
18 namespace app_list { | 25 namespace app_list { |
19 namespace test { | 26 namespace test { |
20 | 27 |
21 namespace { | 28 namespace { |
22 | 29 |
23 const int kInitialItems = 2; | 30 const int kInitialItems = 2; |
24 | 31 |
| 32 class GridViewVisibleWaiter { |
| 33 public: |
| 34 explicit GridViewVisibleWaiter(AppsGridView* grid_view) |
| 35 : grid_view_(grid_view) {} |
| 36 ~GridViewVisibleWaiter() {} |
| 37 |
| 38 void Wait() { |
| 39 if (grid_view_->visible()) |
| 40 return; |
| 41 |
| 42 check_timer_.Start(FROM_HERE, |
| 43 base::TimeDelta::FromMilliseconds(50), |
| 44 base::Bind(&GridViewVisibleWaiter::OnTimerCheck, |
| 45 base::Unretained(this))); |
| 46 run_loop_.reset(new base::RunLoop); |
| 47 run_loop_->Run(); |
| 48 check_timer_.Stop(); |
| 49 } |
| 50 |
| 51 private: |
| 52 void OnTimerCheck() { |
| 53 if (grid_view_->visible()) |
| 54 run_loop_->Quit(); |
| 55 } |
| 56 |
| 57 AppsGridView* grid_view_; |
| 58 scoped_ptr<base::RunLoop> run_loop_; |
| 59 base::RepeatingTimer<GridViewVisibleWaiter> check_timer_; |
| 60 |
| 61 DISALLOW_COPY_AND_ASSIGN(GridViewVisibleWaiter); |
| 62 }; |
| 63 |
25 class AppListMainViewTest : public views::ViewsTestBase { | 64 class AppListMainViewTest : public views::ViewsTestBase { |
26 public: | 65 public: |
27 AppListMainViewTest() | 66 AppListMainViewTest() |
28 : widget_(NULL), | 67 : widget_(NULL), |
29 main_view_(NULL) {} | 68 main_view_(NULL) {} |
30 | 69 |
31 virtual ~AppListMainViewTest() {} | 70 virtual ~AppListMainViewTest() {} |
32 | 71 |
33 // testing::Test overrides: | 72 // testing::Test overrides: |
34 virtual void SetUp() OVERRIDE { | 73 virtual void SetUp() OVERRIDE { |
35 views::ViewsTestBase::SetUp(); | 74 views::ViewsTestBase::SetUp(); |
36 delegate_.reset(new AppListTestViewDelegate); | 75 delegate_.reset(new AppListTestViewDelegate); |
37 delegate_->GetTestModel()->PopulateApps(kInitialItems); | |
38 | 76 |
39 main_view_ = | 77 main_view_ = |
40 new AppListMainView(delegate_.get(), &pagination_model_, GetContext()); | 78 new AppListMainView(delegate_.get(), &pagination_model_, GetContext()); |
41 main_view_->SetPaintToLayer(true); | 79 main_view_->SetPaintToLayer(true); |
42 | 80 |
43 widget_ = new views::Widget; | 81 widget_ = new views::Widget; |
44 views::Widget::InitParams params = | 82 views::Widget::InitParams params = |
45 CreateParams(views::Widget::InitParams::TYPE_POPUP); | 83 CreateParams(views::Widget::InitParams::TYPE_POPUP); |
46 params.bounds.set_size(main_view_->GetPreferredSize()); | 84 params.bounds.set_size(main_view_->GetPreferredSize()); |
47 widget_->Init(params); | 85 widget_->Init(params); |
48 | 86 |
49 widget_->SetContentsView(main_view_); | 87 widget_->SetContentsView(main_view_); |
50 } | 88 } |
51 | 89 |
52 virtual void TearDown() OVERRIDE { | 90 virtual void TearDown() OVERRIDE { |
53 widget_->Close(); | 91 widget_->Close(); |
54 views::ViewsTestBase::TearDown(); | 92 views::ViewsTestBase::TearDown(); |
55 delegate_.reset(); | 93 delegate_.reset(); |
56 } | 94 } |
57 | 95 |
58 const views::ViewModel* ViewModel() { | 96 // |point| is in |grid_view|'s coordinates. |
59 return main_view_->contents_view()->apps_container_view()->apps_grid_view() | 97 AppListItemView* GetItemViewAtPointInGrid(AppsGridView* grid_view, |
60 ->view_model_for_test(); | 98 const gfx::Point& point) { |
| 99 const views::ViewModel* view_model = grid_view->view_model_for_test(); |
| 100 for (int i = 0; i < view_model->view_size(); ++i) { |
| 101 views::View* view = view_model->view_at(i); |
| 102 if (view->bounds().Contains(point)) { |
| 103 return static_cast<AppListItemView*>(view); |
| 104 } |
| 105 } |
| 106 |
| 107 return NULL; |
| 108 } |
| 109 |
| 110 void SimulateClick(views::View* view) { |
| 111 gfx::Point center = view->GetLocalBounds().CenterPoint(); |
| 112 view->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED, |
| 113 center, |
| 114 center, |
| 115 ui::EF_LEFT_MOUSE_BUTTON, |
| 116 ui::EF_LEFT_MOUSE_BUTTON)); |
| 117 view->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED, |
| 118 center, |
| 119 center, |
| 120 ui::EF_LEFT_MOUSE_BUTTON, |
| 121 ui::EF_LEFT_MOUSE_BUTTON)); |
| 122 } |
| 123 |
| 124 // |point| is in |grid_view|'s coordinates. |
| 125 AppListItemView* SimulateInitiateDrag(AppsGridView* grid_view, |
| 126 AppsGridView::Pointer pointer, |
| 127 const gfx::Point& point) { |
| 128 AppListItemView* view = GetItemViewAtPointInGrid(grid_view, point); |
| 129 DCHECK(view); |
| 130 |
| 131 gfx::Point translated = |
| 132 gfx::PointAtOffsetFromOrigin(point - view->bounds().origin()); |
| 133 ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, translated, point, 0, 0); |
| 134 grid_view->InitiateDrag(view, pointer, pressed_event); |
| 135 return view; |
| 136 } |
| 137 |
| 138 // |point| is in |grid_view|'s coordinates. |
| 139 void SimulateUpdateDrag(AppsGridView* grid_view, |
| 140 AppsGridView::Pointer pointer, |
| 141 AppListItemView* drag_view, |
| 142 const gfx::Point& point) { |
| 143 DCHECK(drag_view); |
| 144 gfx::Point translated = |
| 145 gfx::PointAtOffsetFromOrigin(point - drag_view->bounds().origin()); |
| 146 ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, translated, point, 0, 0); |
| 147 grid_view->UpdateDragFromItem(pointer, drag_event); |
| 148 } |
| 149 |
| 150 AppsGridView* RootGridView() { |
| 151 return main_view_->contents_view()->apps_container_view()->apps_grid_view(); |
| 152 } |
| 153 |
| 154 AppListFolderView* FolderView() { |
| 155 return main_view_->contents_view() |
| 156 ->apps_container_view() |
| 157 ->app_list_folder_view(); |
| 158 } |
| 159 |
| 160 AppsGridView* FolderGridView() { return FolderView()->items_grid_view(); } |
| 161 |
| 162 const views::ViewModel* RootViewModel() { |
| 163 return RootGridView()->view_model_for_test(); |
| 164 } |
| 165 |
| 166 const views::ViewModel* FolderViewModel() { |
| 167 return FolderGridView()->view_model_for_test(); |
61 } | 168 } |
62 | 169 |
63 protected: | 170 protected: |
64 views::Widget* widget_; // Owned by native window. | 171 views::Widget* widget_; // Owned by native window. |
65 AppListMainView* main_view_; // Owned by |widget_|. | 172 AppListMainView* main_view_; // Owned by |widget_|. |
66 PaginationModel pagination_model_; | 173 PaginationModel pagination_model_; |
67 scoped_ptr<AppListTestViewDelegate> delegate_; | 174 scoped_ptr<AppListTestViewDelegate> delegate_; |
68 | 175 |
69 private: | 176 private: |
70 DISALLOW_COPY_AND_ASSIGN(AppListMainViewTest); | 177 DISALLOW_COPY_AND_ASSIGN(AppListMainViewTest); |
71 }; | 178 }; |
72 | 179 |
73 } // namespace | 180 } // namespace |
74 | 181 |
75 // Tests changing the AppListModel when switching profiles. | 182 // Tests changing the AppListModel when switching profiles. |
76 TEST_F(AppListMainViewTest, ModelChanged) { | 183 TEST_F(AppListMainViewTest, ModelChanged) { |
77 EXPECT_EQ(kInitialItems, ViewModel()->view_size()); | 184 delegate_->GetTestModel()->PopulateApps(kInitialItems); |
| 185 EXPECT_EQ(kInitialItems, RootViewModel()->view_size()); |
78 | 186 |
79 // The model is owned by a profile keyed service, which is never destroyed | 187 // The model is owned by a profile keyed service, which is never destroyed |
80 // until after profile switching. | 188 // until after profile switching. |
81 scoped_ptr<AppListModel> old_model(delegate_->ReleaseTestModel()); | 189 scoped_ptr<AppListModel> old_model(delegate_->ReleaseTestModel()); |
82 | 190 |
83 const int kReplacementItems = 5; | 191 const int kReplacementItems = 5; |
84 delegate_->ReplaceTestModel(kReplacementItems); | 192 delegate_->ReplaceTestModel(kReplacementItems); |
85 main_view_->ModelChanged(); | 193 main_view_->ModelChanged(); |
86 EXPECT_EQ(kReplacementItems, ViewModel()->view_size()); | 194 EXPECT_EQ(kReplacementItems, RootViewModel()->view_size()); |
| 195 } |
| 196 |
| 197 // Tests dragging an item out of a single item folder and drop it at the last |
| 198 // slot. |
| 199 TEST_F(AppListMainViewTest, DragLastItemFromFolderAndDropAtLastSlot) { |
| 200 // Prepare single folder with a single item in it. |
| 201 AppListFolderItem* folder_item = |
| 202 delegate_->GetTestModel()->CreateSingleItemFolder("single_item_folder", |
| 203 "single"); |
| 204 EXPECT_EQ(folder_item, |
| 205 delegate_->GetTestModel()->FindFolderItem("single_item_folder")); |
| 206 EXPECT_EQ(AppListFolderItem::kItemType, folder_item->GetItemType()); |
| 207 |
| 208 EXPECT_EQ(1, RootViewModel()->view_size()); |
| 209 AppListItemView* folder_item_view = |
| 210 static_cast<AppListItemView*>(RootViewModel()->view_at(0)); |
| 211 EXPECT_EQ(folder_item_view->item(), folder_item); |
| 212 const gfx::Rect first_slot_tile = folder_item_view->bounds(); |
| 213 |
| 214 // Click on the folder to open it. |
| 215 EXPECT_FALSE(FolderView()->visible()); |
| 216 SimulateClick(folder_item_view); |
| 217 base::RunLoop().RunUntilIdle(); |
| 218 EXPECT_TRUE(FolderView()->visible()); |
| 219 |
| 220 #if defined(OS_WIN) |
| 221 AppsGridViewTestApi folder_grid_view_test_api(FolderGridView()); |
| 222 folder_grid_view_test_api.DisableSynchronousDrag(); |
| 223 #endif |
| 224 |
| 225 // Start to drag the item in folder. |
| 226 EXPECT_EQ(1, FolderViewModel()->view_size()); |
| 227 views::View* item_view = FolderViewModel()->view_at(0); |
| 228 gfx::Point point = item_view->bounds().CenterPoint(); |
| 229 AppListItemView* dragged = |
| 230 SimulateInitiateDrag(FolderGridView(), AppsGridView::MOUSE, point); |
| 231 EXPECT_EQ(item_view, dragged); |
| 232 EXPECT_FALSE(RootGridView()->visible()); |
| 233 EXPECT_TRUE(FolderView()->visible()); |
| 234 |
| 235 // Drag it to top left corner. |
| 236 point = gfx::Point(0, 0); |
| 237 // Two update drags needed to actually drag the view. The first changes state |
| 238 // and the 2nd one actually moves the view. The 2nd call can be removed when |
| 239 // UpdateDrag is fixed. |
| 240 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point); |
| 241 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point); |
| 242 base::RunLoop().RunUntilIdle(); |
| 243 |
| 244 // Wait until the folder view is invisible and root grid view shows up. |
| 245 GridViewVisibleWaiter(RootGridView()).Wait(); |
| 246 EXPECT_TRUE(RootGridView()->visible()); |
| 247 EXPECT_EQ(0, FolderView()->layer()->opacity()); |
| 248 |
| 249 // Drop it to the slot on the right of first slot. |
| 250 gfx::Rect drop_target_tile(first_slot_tile); |
| 251 drop_target_tile.Offset(first_slot_tile.width(), 0); |
| 252 point = drop_target_tile.CenterPoint(); |
| 253 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point); |
| 254 SimulateUpdateDrag(FolderGridView(), AppsGridView::MOUSE, dragged, point); |
| 255 base::RunLoop().RunUntilIdle(); |
| 256 |
| 257 // Drop it. |
| 258 FolderGridView()->EndDrag(false); |
| 259 base::RunLoop().RunUntilIdle(); |
| 260 |
| 261 // Folder icon view should be gone and there is only one item view. |
| 262 EXPECT_EQ(1, RootViewModel()->view_size()); |
| 263 EXPECT_EQ(AppListItemView::kViewClassName, |
| 264 RootViewModel()->view_at(0)->GetClassName()); |
| 265 |
| 266 // The item view should be in slot 1 instead of slot 2 where it is dropped. |
| 267 AppsGridViewTestApi root_grid_view_test_api(RootGridView()); |
| 268 root_grid_view_test_api.LayoutToIdealBounds(); |
| 269 EXPECT_EQ(first_slot_tile, RootViewModel()->view_at(0)->bounds()); |
| 270 |
| 271 // Single item folder should be auto removed. |
| 272 EXPECT_EQ(NULL, |
| 273 delegate_->GetTestModel()->FindFolderItem("single_item_folder")); |
87 } | 274 } |
88 | 275 |
89 } // namespace test | 276 } // namespace test |
90 } // namespace app_list | 277 } // namespace app_list |
OLD | NEW |