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
3 // found in the LICENSE file.
5 #include "ui/app_list/views/app_list_main_view.h"
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"
11 #include "testing/gtest/include/gtest/gtest.h"
12 #include "ui/app_list/pagination_model.h"
13 #include "ui/app_list/test/app_list_test_model.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"
17 #include "ui/app_list/views/apps_container_view.h"
18 #include "ui/app_list/views/apps_grid_view.h"
19 #include "ui/app_list/views/contents_view.h"
20 #include "ui/app_list/views/test/apps_grid_view_test_api.h"
21 #include "ui/views/test/views_test_base.h"
22 #include "ui/views/view_model.h"
23 #include "ui/views/widget/widget.h"
30 const int kInitialItems
= 2;
32 class GridViewVisibleWaiter
{
34 explicit GridViewVisibleWaiter(AppsGridView
* grid_view
)
35 : grid_view_(grid_view
) {}
36 ~GridViewVisibleWaiter() {}
39 if (grid_view_
->visible())
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
);
53 if (grid_view_
->visible())
57 AppsGridView
* grid_view_
;
58 scoped_ptr
<base::RunLoop
> run_loop_
;
59 base::RepeatingTimer
<GridViewVisibleWaiter
> check_timer_
;
61 DISALLOW_COPY_AND_ASSIGN(GridViewVisibleWaiter
);
64 class AppListMainViewTest
: public views::ViewsTestBase
{
70 virtual ~AppListMainViewTest() {}
72 // testing::Test overrides:
73 virtual void SetUp() OVERRIDE
{
74 views::ViewsTestBase::SetUp();
75 delegate_
.reset(new AppListTestViewDelegate
);
78 new AppListMainView(delegate_
.get(), &pagination_model_
, GetContext());
79 main_view_
->SetPaintToLayer(true);
81 widget_
= new views::Widget
;
82 views::Widget::InitParams params
=
83 CreateParams(views::Widget::InitParams::TYPE_POPUP
);
84 params
.bounds
.set_size(main_view_
->GetPreferredSize());
85 widget_
->Init(params
);
87 widget_
->SetContentsView(main_view_
);
90 virtual void TearDown() OVERRIDE
{
92 views::ViewsTestBase::TearDown();
96 // |point| is in |grid_view|'s coordinates.
97 AppListItemView
* GetItemViewAtPointInGrid(AppsGridView
* grid_view
,
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
);
110 void SimulateClick(views::View
* view
) {
111 gfx::Point center
= view
->GetLocalBounds().CenterPoint();
112 view
->OnMousePressed(ui::MouseEvent(ui::ET_MOUSE_PRESSED
,
115 ui::EF_LEFT_MOUSE_BUTTON
,
116 ui::EF_LEFT_MOUSE_BUTTON
));
117 view
->OnMouseReleased(ui::MouseEvent(ui::ET_MOUSE_RELEASED
,
120 ui::EF_LEFT_MOUSE_BUTTON
,
121 ui::EF_LEFT_MOUSE_BUTTON
));
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
);
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
);
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
) {
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
);
150 AppsGridView
* RootGridView() {
151 return main_view_
->contents_view()->apps_container_view()->apps_grid_view();
154 AppListFolderView
* FolderView() {
155 return main_view_
->contents_view()
156 ->apps_container_view()
157 ->app_list_folder_view();
160 AppsGridView
* FolderGridView() { return FolderView()->items_grid_view(); }
162 const views::ViewModel
* RootViewModel() {
163 return RootGridView()->view_model_for_test();
166 const views::ViewModel
* FolderViewModel() {
167 return FolderGridView()->view_model_for_test();
171 views::Widget
* widget_
; // Owned by native window.
172 AppListMainView
* main_view_
; // Owned by |widget_|.
173 PaginationModel pagination_model_
;
174 scoped_ptr
<AppListTestViewDelegate
> delegate_
;
177 DISALLOW_COPY_AND_ASSIGN(AppListMainViewTest
);
182 // Tests changing the AppListModel when switching profiles.
183 TEST_F(AppListMainViewTest
, ModelChanged
) {
184 delegate_
->GetTestModel()->PopulateApps(kInitialItems
);
185 EXPECT_EQ(kInitialItems
, RootViewModel()->view_size());
187 // The model is owned by a profile keyed service, which is never destroyed
188 // until after profile switching.
189 scoped_ptr
<AppListModel
> old_model(delegate_
->ReleaseTestModel());
191 const int kReplacementItems
= 5;
192 delegate_
->ReplaceTestModel(kReplacementItems
);
193 main_view_
->ModelChanged();
194 EXPECT_EQ(kReplacementItems
, RootViewModel()->view_size());
197 // Tests dragging an item out of a single item folder and drop it at the last
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",
204 EXPECT_EQ(folder_item
,
205 delegate_
->GetTestModel()->FindFolderItem("single_item_folder"));
206 EXPECT_EQ(AppListFolderItem::kItemType
, folder_item
->GetItemType());
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();
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());
221 AppsGridViewTestApi
folder_grid_view_test_api(FolderGridView());
222 folder_grid_view_test_api
.DisableSynchronousDrag();
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());
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();
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());
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();
258 FolderGridView()->EndDrag(false);
259 base::RunLoop().RunUntilIdle();
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());
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());
271 // Single item folder should be auto removed.
273 delegate_
->GetTestModel()->FindFolderItem("single_item_folder"));
277 } // namespace app_list