Experimental app list slide animation is now horizontal, not vertical.
[chromium-blink-merge.git] / ui / app_list / views / contents_view.cc
blob61ca47d4a7738abee73ba62c63901578aa3df46d
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.
5 #include "ui/app_list/views/contents_view.h"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "ui/app_list/app_list_constants.h"
11 #include "ui/app_list/app_list_switches.h"
12 #include "ui/app_list/app_list_view_delegate.h"
13 #include "ui/app_list/pagination_model.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/search_result_list_view.h"
19 #include "ui/app_list/views/start_page_view.h"
20 #include "ui/events/event.h"
21 #include "ui/views/animation/bounds_animator.h"
22 #include "ui/views/view_model.h"
23 #include "ui/views/view_model_utils.h"
25 namespace app_list {
27 namespace {
29 // Indexes of interesting views in ViewModel of ContentsView.
30 const int kIndexAppsContainer = 0;
31 const int kIndexSearchResults = 1;
32 const int kIndexStartPage = 2;
34 const int kMinMouseWheelToSwitchPage = 20;
35 const int kMinScrollToSwitchPage = 20;
36 const int kMinHorizVelocityToSwitchPage = 800;
38 const double kFinishTransitionThreshold = 0.33;
40 AppsContainerView* GetAppsContainerView(views::ViewModel* model) {
41 return static_cast<AppsContainerView*>(model->view_at(kIndexAppsContainer));
44 SearchResultListView* GetSearchResultListView(views::ViewModel* model) {
45 return static_cast<SearchResultListView*>(
46 model->view_at(kIndexSearchResults));
49 StartPageView* GetStartPageView(views::ViewModel* model) {
50 return static_cast<StartPageView*>(model->view_at(kIndexStartPage));
53 } // namespace
55 ContentsView::ContentsView(AppListMainView* app_list_main_view,
56 PaginationModel* pagination_model,
57 AppListModel* model,
58 AppListViewDelegate* view_delegate)
59 : show_state_(SHOW_APPS),
60 pagination_model_(pagination_model),
61 start_page_view_(NULL),
62 app_list_main_view_(app_list_main_view),
63 view_model_(new views::ViewModel),
64 bounds_animator_(new views::BoundsAnimator(this)) {
65 DCHECK(model);
66 pagination_model_->SetTransitionDurations(
67 kPageTransitionDurationInMs,
68 kOverscrollPageTransitionDurationMs);
70 apps_container_view_ =
71 new AppsContainerView(app_list_main_view, pagination_model, model);
72 AddChildView(apps_container_view_);
73 view_model_->Add(apps_container_view_, kIndexAppsContainer);
75 SearchResultListView* search_results_view = new SearchResultListView(
76 app_list_main_view, view_delegate);
77 AddChildView(search_results_view);
78 view_model_->Add(search_results_view, kIndexSearchResults);
80 if (app_list::switches::IsExperimentalAppListEnabled()) {
81 content::WebContents* start_page_contents =
82 view_delegate->GetStartPageContents();
83 start_page_view_ =
84 new StartPageView(app_list_main_view, start_page_contents);
85 AddChildView(start_page_view_);
86 view_model_->Add(start_page_view_, kIndexStartPage);
89 GetSearchResultListView(view_model_.get())->SetResults(model->results());
92 ContentsView::~ContentsView() {
95 void ContentsView::CancelDrag() {
96 if (apps_container_view_->apps_grid_view()->has_dragged_view())
97 apps_container_view_->apps_grid_view()->EndDrag(true);
98 if (apps_container_view_->app_list_folder_view()
99 ->items_grid_view()
100 ->has_dragged_view()) {
101 apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag(
102 true);
106 void ContentsView::SetDragAndDropHostOfCurrentAppList(
107 ApplicationDragAndDropHost* drag_and_drop_host) {
108 apps_container_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
111 void ContentsView::SetShowState(ShowState show_state) {
112 if (show_state_ == show_state)
113 return;
115 show_state_ = show_state;
116 ShowStateChanged();
119 void ContentsView::ShowStateChanged() {
120 SearchResultListView* results_view =
121 GetSearchResultListView(view_model_.get());
122 // TODO(xiyuan): Highlight default match instead of the first.
123 if (show_state_ == SHOW_SEARCH_RESULTS && results_view->visible())
124 results_view->SetSelectedIndex(0);
125 results_view->UpdateAutoLaunchState();
127 // Notify parent AppListMainView of show state change.
128 app_list_main_view_->OnContentsViewShowStateChanged();
130 if (show_state_ == SHOW_START_PAGE)
131 GetStartPageView(view_model_.get())->Reset();
133 AnimateToIdealBounds();
136 void ContentsView::CalculateIdealBounds() {
137 gfx::Rect rect(GetContentsBounds());
138 if (rect.IsEmpty())
139 return;
141 if (app_list::switches::IsExperimentalAppListEnabled()) {
142 int incoming_view_index = 0;
143 switch (show_state_) {
144 case SHOW_APPS:
145 incoming_view_index = kIndexAppsContainer;
146 break;
147 case SHOW_SEARCH_RESULTS:
148 incoming_view_index = kIndexSearchResults;
149 break;
150 case SHOW_START_PAGE:
151 incoming_view_index = kIndexStartPage;
152 break;
153 default:
154 NOTREACHED();
157 gfx::Rect incoming_target(rect);
158 gfx::Rect outgoing_target(rect);
159 outgoing_target.set_x(-outgoing_target.width());
161 for (int i = 0; i < view_model_->view_size(); ++i) {
162 view_model_->set_ideal_bounds(i,
163 i == incoming_view_index ? incoming_target
164 : outgoing_target);
166 return;
169 gfx::Rect container_frame(rect);
170 gfx::Rect results_frame(rect);
172 // Offsets apps grid and result list based on |show_state_|.
173 // SearchResultListView is on top of apps grid. Visible view is left in
174 // visible area and invisible ones is put out of the visible area.
175 int contents_area_height = rect.height();
176 switch (show_state_) {
177 case SHOW_APPS:
178 results_frame.Offset(0, -contents_area_height);
179 break;
180 case SHOW_SEARCH_RESULTS:
181 container_frame.Offset(0, contents_area_height);
182 break;
183 default:
184 NOTREACHED() << "Unknown show_state_ " << show_state_;
185 break;
188 view_model_->set_ideal_bounds(kIndexAppsContainer, container_frame);
189 view_model_->set_ideal_bounds(kIndexSearchResults, results_frame);
192 void ContentsView::AnimateToIdealBounds() {
193 CalculateIdealBounds();
194 for (int i = 0; i < view_model_->view_size(); ++i) {
195 bounds_animator_->AnimateViewTo(view_model_->view_at(i),
196 view_model_->ideal_bounds(i));
200 void ContentsView::ShowSearchResults(bool show) {
201 SetShowState(show ? SHOW_SEARCH_RESULTS : SHOW_APPS);
204 void ContentsView::ShowFolderContent(AppListFolderItem* item) {
205 apps_container_view_->ShowActiveFolder(item);
208 void ContentsView::Prerender() {
209 const int selected_page = std::max(0, pagination_model_->selected_page());
210 apps_container_view_->apps_grid_view()->Prerender(selected_page);
213 gfx::Size ContentsView::GetPreferredSize() const {
214 const gfx::Size container_size = GetAppsContainerView(view_model_.get())->
215 apps_grid_view()->GetPreferredSize();
216 const gfx::Size results_size =
217 GetSearchResultListView(view_model_.get())->GetPreferredSize();
219 int width = std::max(container_size.width(), results_size.width());
220 int height = std::max(container_size.height(), results_size.height());
221 return gfx::Size(width, height);
224 void ContentsView::Layout() {
225 CalculateIdealBounds();
226 views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
229 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
230 switch (show_state_) {
231 case SHOW_APPS:
232 return GetAppsContainerView(view_model_.get())->OnKeyPressed(event);
233 case SHOW_SEARCH_RESULTS:
234 return GetSearchResultListView(view_model_.get())->OnKeyPressed(event);
235 case SHOW_START_PAGE:
236 return GetStartPageView(view_model_.get())->OnKeyPressed(event);
237 default:
238 NOTREACHED() << "Unknown show state " << show_state_;
240 return false;
243 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
244 if (show_state_ != SHOW_APPS)
245 return false;
247 int offset;
248 if (abs(event.x_offset()) > abs(event.y_offset()))
249 offset = event.x_offset();
250 else
251 offset = event.y_offset();
253 if (abs(offset) > kMinMouseWheelToSwitchPage) {
254 if (!pagination_model_->has_transition()) {
255 pagination_model_->SelectPageRelative(
256 offset > 0 ? -1 : 1, true);
258 return true;
261 return false;
264 void ContentsView::OnGestureEvent(ui::GestureEvent* event) {
265 if (show_state_ != SHOW_APPS)
266 return;
268 switch (event->type()) {
269 case ui::ET_GESTURE_SCROLL_BEGIN:
270 pagination_model_->StartScroll();
271 event->SetHandled();
272 return;
273 case ui::ET_GESTURE_SCROLL_UPDATE:
274 // event->details.scroll_x() > 0 means moving contents to right. That is,
275 // transitioning to previous page.
276 pagination_model_->UpdateScroll(
277 event->details().scroll_x() / GetContentsBounds().width());
278 event->SetHandled();
279 return;
280 case ui::ET_GESTURE_SCROLL_END:
281 pagination_model_->EndScroll(pagination_model_->
282 transition().progress < kFinishTransitionThreshold);
283 event->SetHandled();
284 return;
285 case ui::ET_SCROLL_FLING_START: {
286 pagination_model_->EndScroll(true);
287 if (fabs(event->details().velocity_x()) > kMinHorizVelocityToSwitchPage) {
288 pagination_model_->SelectPageRelative(
289 event->details().velocity_x() < 0 ? 1 : -1,
290 true);
292 event->SetHandled();
293 return;
295 default:
296 break;
300 void ContentsView::OnScrollEvent(ui::ScrollEvent* event) {
301 if (show_state_ != SHOW_APPS ||
302 event->type() == ui::ET_SCROLL_FLING_CANCEL) {
303 return;
306 float offset;
307 if (std::abs(event->x_offset()) > std::abs(event->y_offset()))
308 offset = event->x_offset();
309 else
310 offset = event->y_offset();
312 if (std::abs(offset) > kMinScrollToSwitchPage) {
313 if (!pagination_model_->has_transition()) {
314 pagination_model_->SelectPageRelative(offset > 0 ? -1 : 1,
315 true);
317 event->SetHandled();
318 event->StopPropagation();
322 } // namespace app_list