a2418e04b650afdbda1151c6ab65ba375477f3ab
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / contents_view.cc
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/contents_view.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "base/logging.h"
11 #include "grit/ui_resources.h"
12 #include "ui/app_list/app_list_constants.h"
13 #include "ui/app_list/app_list_switches.h"
14 #include "ui/app_list/app_list_view_delegate.h"
15 #include "ui/app_list/views/app_list_folder_view.h"
16 #include "ui/app_list/views/app_list_main_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_switcher_view.h"
20 #include "ui/app_list/views/search_result_list_view.h"
21 #include "ui/app_list/views/start_page_view.h"
22 #include "ui/events/event.h"
23 #include "ui/views/view_model.h"
24 #include "ui/views/view_model_utils.h"
25
26 namespace app_list {
27
28 namespace {
29
30 const int kMinMouseWheelToSwitchPage = 20;
31 const int kMinScrollToSwitchPage = 20;
32 const int kMinHorizVelocityToSwitchPage = 800;
33
34 const double kFinishTransitionThreshold = 0.33;
35
36 }  // namespace
37
38 ContentsView::ContentsView(AppListMainView* app_list_main_view)
39     : search_results_view_(NULL),
40       start_page_view_(NULL),
41       app_list_main_view_(app_list_main_view),
42       contents_switcher_view_(NULL),
43       view_model_(new views::ViewModel),
44       page_before_search_(0) {
45   pagination_model_.AddObserver(this);
46 }
47
48 ContentsView::~ContentsView() {
49   pagination_model_.RemoveObserver(this);
50   if (contents_switcher_view_)
51     pagination_model_.RemoveObserver(contents_switcher_view_);
52 }
53
54 void ContentsView::InitNamedPages(AppListModel* model,
55                                   AppListViewDelegate* view_delegate) {
56   DCHECK(model);
57
58   if (app_list::switches::IsExperimentalAppListEnabled()) {
59     std::vector<views::View*> custom_page_views =
60         view_delegate->CreateCustomPageWebViews(GetLocalBounds().size());
61     for (std::vector<views::View*>::const_iterator it =
62              custom_page_views.begin();
63          it != custom_page_views.end();
64          ++it) {
65       AddLauncherPage(*it, IDR_APP_LIST_NOTIFICATIONS_ICON);
66     }
67
68     start_page_view_ = new StartPageView(app_list_main_view_, view_delegate);
69     AddLauncherPage(
70         start_page_view_, IDR_APP_LIST_SEARCH_ICON, NAMED_PAGE_START);
71   } else {
72     search_results_view_ =
73         new SearchResultListView(app_list_main_view_, view_delegate);
74     AddLauncherPage(search_results_view_, 0, NAMED_PAGE_SEARCH_RESULTS);
75     search_results_view_->SetResults(model->results());
76   }
77
78   apps_container_view_ = new AppsContainerView(app_list_main_view_, model);
79
80   int initial_page_index = AddLauncherPage(
81       apps_container_view_, IDR_APP_LIST_APPS_ICON, NAMED_PAGE_APPS);
82   if (app_list::switches::IsExperimentalAppListEnabled())
83     initial_page_index = GetPageIndexForNamedPage(NAMED_PAGE_START);
84
85   page_before_search_ = initial_page_index;
86   pagination_model_.SelectPage(initial_page_index, false);
87
88   // Needed to update the main search box visibility.
89   ActivePageChanged(false);
90 }
91
92 void ContentsView::CancelDrag() {
93   if (apps_container_view_->apps_grid_view()->has_dragged_view())
94     apps_container_view_->apps_grid_view()->EndDrag(true);
95   if (apps_container_view_->app_list_folder_view()
96           ->items_grid_view()
97           ->has_dragged_view()) {
98     apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag(
99         true);
100   }
101 }
102
103 void ContentsView::SetDragAndDropHostOfCurrentAppList(
104     ApplicationDragAndDropHost* drag_and_drop_host) {
105   apps_container_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
106 }
107
108 void ContentsView::SetContentsSwitcherView(
109     ContentsSwitcherView* contents_switcher_view) {
110   DCHECK(!contents_switcher_view_);
111   contents_switcher_view_ = contents_switcher_view;
112   if (contents_switcher_view_)
113     pagination_model_.AddObserver(contents_switcher_view_);
114 }
115
116 void ContentsView::SetActivePage(int page_index) {
117   if (GetActivePageIndex() == page_index)
118     return;
119
120   SetActivePageInternal(page_index, false);
121 }
122
123 int ContentsView::GetActivePageIndex() const {
124   // The active page is changed at the beginning of an animation, not the end.
125   return pagination_model_.SelectedTargetPage();
126 }
127
128 bool ContentsView::IsNamedPageActive(NamedPage named_page) const {
129   std::map<NamedPage, int>::const_iterator it =
130       named_page_to_view_.find(named_page);
131   if (it == named_page_to_view_.end())
132     return false;
133   return it->second == GetActivePageIndex();
134 }
135
136 int ContentsView::GetPageIndexForNamedPage(NamedPage named_page) const {
137   // Find the index of the view corresponding to the given named_page.
138   std::map<NamedPage, int>::const_iterator it =
139       named_page_to_view_.find(named_page);
140   // GetPageIndexForNamedPage should never be called on a named_page that does
141   // not have a corresponding view.
142   DCHECK(it != named_page_to_view_.end());
143   return it->second;
144 }
145
146 int ContentsView::NumLauncherPages() const {
147   return pagination_model_.total_pages();
148 }
149
150 void ContentsView::SetActivePageInternal(int page_index,
151                                          bool show_search_results) {
152   if (!show_search_results)
153     page_before_search_ = page_index;
154   // Start animating to the new page.
155   pagination_model_.SelectPage(page_index, true);
156   ActivePageChanged(show_search_results);
157 }
158
159 void ContentsView::ActivePageChanged(bool show_search_results) {
160   // TODO(xiyuan): Highlight default match instead of the first.
161   if (IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS) &&
162       search_results_view_->visible()) {
163     search_results_view_->SetSelectedIndex(0);
164   }
165   if (search_results_view_)
166     search_results_view_->UpdateAutoLaunchState();
167
168   if (IsNamedPageActive(NAMED_PAGE_START)) {
169     if (show_search_results)
170       start_page_view_->ShowSearchResults();
171     else
172       start_page_view_->Reset();
173   }
174
175   // Notify parent AppListMainView of the page change.
176   app_list_main_view_->UpdateSearchBoxVisibility();
177 }
178
179 void ContentsView::ShowSearchResults(bool show) {
180   int search_page = GetPageIndexForNamedPage(
181       app_list::switches::IsExperimentalAppListEnabled()
182           ? NAMED_PAGE_START
183           : NAMED_PAGE_SEARCH_RESULTS);
184
185   SetActivePageInternal(show ? search_page : page_before_search_, show);
186 }
187
188 bool ContentsView::IsShowingSearchResults() const {
189   return app_list::switches::IsExperimentalAppListEnabled()
190              ? IsNamedPageActive(NAMED_PAGE_START) &&
191                    start_page_view_->IsShowingSearchResults()
192              : IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS);
193 }
194
195 void ContentsView::UpdatePageBounds() {
196   gfx::Rect rect(GetContentsBounds());
197   if (rect.IsEmpty())
198     return;
199
200   // The bounds calculations will potentially be mid-transition (depending on
201   // the state of the PaginationModel).
202   int current_page = std::max(0, pagination_model_.selected_page());
203   int target_page = current_page;
204   double progress = 1;
205   if (pagination_model_.has_transition()) {
206     const PaginationModel::Transition& transition =
207         pagination_model_.transition();
208     if (pagination_model_.is_valid_page(transition.target_page)) {
209       target_page = transition.target_page;
210       progress = transition.progress;
211     }
212   }
213
214   gfx::Rect incoming_target(rect);
215   gfx::Rect outgoing_target(rect);
216   int dir = target_page > current_page ? -1 : 1;
217
218   if (app_list::switches::IsExperimentalAppListEnabled()) {
219     // The experimental app list transitions horizontally.
220     int page_width = rect.width();
221     int transition_offset = progress * page_width * dir;
222
223     outgoing_target.set_x(transition_offset);
224     incoming_target.set_x(dir < 0 ? transition_offset + page_width
225                                   : transition_offset - page_width);
226   } else {
227     // The normal app list transitions vertically.
228     int page_height = rect.height();
229     int transition_offset = progress * page_height * dir;
230
231     outgoing_target.set_y(transition_offset);
232     incoming_target.set_y(dir < 0 ? transition_offset + page_height
233                                   : transition_offset - page_height);
234   }
235
236   view_model_->view_at(current_page)->SetBoundsRect(outgoing_target);
237   view_model_->view_at(target_page)->SetBoundsRect(incoming_target);
238 }
239
240 PaginationModel* ContentsView::GetAppsPaginationModel() {
241   return apps_container_view_->apps_grid_view()->pagination_model();
242 }
243
244 void ContentsView::ShowFolderContent(AppListFolderItem* item) {
245   apps_container_view_->ShowActiveFolder(item);
246 }
247
248 void ContentsView::Prerender() {
249   const int selected_page =
250       std::max(0, GetAppsPaginationModel()->selected_page());
251   apps_container_view_->apps_grid_view()->Prerender(selected_page);
252 }
253
254 views::View* ContentsView::GetPageView(int index) {
255   return view_model_->view_at(index);
256 }
257
258 void ContentsView::AddBlankPageForTesting() {
259   AddLauncherPage(new views::View, 0);
260 }
261
262 int ContentsView::AddLauncherPage(views::View* view, int resource_id) {
263   int page_index = view_model_->view_size();
264   AddChildView(view);
265   view_model_->Add(view, page_index);
266   if (contents_switcher_view_)
267     contents_switcher_view_->AddSwitcherButton(resource_id, page_index);
268   pagination_model_.SetTotalPages(view_model_->view_size());
269   return page_index;
270 }
271
272 int ContentsView::AddLauncherPage(views::View* view,
273                                   int resource_id,
274                                   NamedPage named_page) {
275   int page_index = AddLauncherPage(view, resource_id);
276   named_page_to_view_.insert(std::pair<NamedPage, int>(named_page, page_index));
277   return page_index;
278 }
279
280 gfx::Size ContentsView::GetPreferredSize() const {
281   const gfx::Size container_size =
282       apps_container_view_->apps_grid_view()->GetPreferredSize();
283   const gfx::Size results_size = search_results_view_
284                                      ? search_results_view_->GetPreferredSize()
285                                      : gfx::Size();
286
287   int width = std::max(container_size.width(), results_size.width());
288   int height = std::max(container_size.height(), results_size.height());
289   return gfx::Size(width, height);
290 }
291
292 void ContentsView::Layout() {
293   // Immediately finish all current animations.
294   pagination_model_.FinishAnimation();
295
296   // Move the current view onto the screen, and all other views off screen to
297   // the left. (Since we are not animating, we don't need to be careful about
298   // which side we place the off-screen views onto.)
299   gfx::Rect rect(GetContentsBounds());
300   if (rect.IsEmpty())
301     return;
302
303   gfx::Rect offscreen_target(rect);
304   offscreen_target.set_x(-rect.width());
305
306   for (int i = 0; i < view_model_->view_size(); ++i) {
307     view_model_->view_at(i)->SetBoundsRect(
308         i == pagination_model_.SelectedTargetPage() ? rect : offscreen_target);
309   }
310 }
311
312 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
313   return view_model_->view_at(GetActivePageIndex())->OnKeyPressed(event);
314 }
315
316 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
317   if (!IsNamedPageActive(NAMED_PAGE_APPS))
318     return false;
319
320   int offset;
321   if (abs(event.x_offset()) > abs(event.y_offset()))
322     offset = event.x_offset();
323   else
324     offset = event.y_offset();
325
326   if (abs(offset) > kMinMouseWheelToSwitchPage) {
327     if (!GetAppsPaginationModel()->has_transition()) {
328       GetAppsPaginationModel()->SelectPageRelative(offset > 0 ? -1 : 1, true);
329     }
330     return true;
331   }
332
333   return false;
334 }
335
336 void ContentsView::TotalPagesChanged() {
337 }
338
339 void ContentsView::SelectedPageChanged(int old_selected, int new_selected) {
340 }
341
342 void ContentsView::TransitionStarted() {
343 }
344
345 void ContentsView::TransitionChanged() {
346   UpdatePageBounds();
347 }
348
349 void ContentsView::OnGestureEvent(ui::GestureEvent* event) {
350   if (!IsNamedPageActive(NAMED_PAGE_APPS))
351     return;
352
353   switch (event->type()) {
354     case ui::ET_GESTURE_SCROLL_BEGIN:
355       GetAppsPaginationModel()->StartScroll();
356       event->SetHandled();
357       return;
358     case ui::ET_GESTURE_SCROLL_UPDATE:
359       // event->details.scroll_x() > 0 means moving contents to right. That is,
360       // transitioning to previous page.
361       GetAppsPaginationModel()->UpdateScroll(event->details().scroll_x() /
362                                              GetContentsBounds().width());
363       event->SetHandled();
364       return;
365     case ui::ET_GESTURE_SCROLL_END:
366       GetAppsPaginationModel()->EndScroll(
367           GetAppsPaginationModel()->transition().progress <
368           kFinishTransitionThreshold);
369       event->SetHandled();
370       return;
371     case ui::ET_SCROLL_FLING_START: {
372       GetAppsPaginationModel()->EndScroll(true);
373       if (fabs(event->details().velocity_x()) > kMinHorizVelocityToSwitchPage) {
374         GetAppsPaginationModel()->SelectPageRelative(
375             event->details().velocity_x() < 0 ? 1 : -1, true);
376       }
377       event->SetHandled();
378       return;
379     }
380     default:
381       break;
382   }
383 }
384
385 void ContentsView::OnScrollEvent(ui::ScrollEvent* event) {
386   if (!IsNamedPageActive(NAMED_PAGE_APPS) ||
387       event->type() == ui::ET_SCROLL_FLING_CANCEL) {
388     return;
389   }
390
391   float offset;
392   if (std::abs(event->x_offset()) > std::abs(event->y_offset()))
393     offset = event->x_offset();
394   else
395     offset = event->y_offset();
396
397   if (std::abs(offset) > kMinScrollToSwitchPage) {
398     if (!GetAppsPaginationModel()->has_transition()) {
399       GetAppsPaginationModel()->SelectPageRelative(offset > 0 ? -1 : 1, true);
400     }
401     event->SetHandled();
402     event->StopPropagation();
403   }
404 }
405
406 }  // namespace app_list