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