Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / app_list / views / contents_view.cc
blob44fef48911dcaa2f6beb3508e45d9cb36682bbb8
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>
8 #include <vector>
10 #include "base/logging.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/custom_launcher_page_view.h"
19 #include "ui/app_list/views/search_box_view.h"
20 #include "ui/app_list/views/search_result_list_view.h"
21 #include "ui/app_list/views/search_result_page_view.h"
22 #include "ui/app_list/views/search_result_tile_item_list_view.h"
23 #include "ui/app_list/views/start_page_view.h"
24 #include "ui/events/event.h"
25 #include "ui/resources/grit/ui_resources.h"
26 #include "ui/views/view_model.h"
27 #include "ui/views/widget/widget.h"
29 namespace app_list {
31 ContentsView::ContentsView(AppListMainView* app_list_main_view)
32 : apps_container_view_(nullptr),
33 search_results_page_view_(nullptr),
34 start_page_view_(nullptr),
35 custom_page_view_(nullptr),
36 app_list_main_view_(app_list_main_view),
37 page_before_search_(0) {
38 pagination_model_.SetTransitionDurations(kPageTransitionDurationInMs,
39 kOverscrollPageTransitionDurationMs);
40 pagination_model_.AddObserver(this);
43 ContentsView::~ContentsView() {
44 pagination_model_.RemoveObserver(this);
47 void ContentsView::Init(AppListModel* model) {
48 DCHECK(model);
50 AppListViewDelegate* view_delegate = app_list_main_view_->view_delegate();
52 if (app_list::switches::IsExperimentalAppListEnabled()) {
53 std::vector<views::View*> custom_page_views =
54 view_delegate->CreateCustomPageWebViews(GetLocalBounds().size());
55 // Only add the first custom page view as STATE_CUSTOM_LAUNCHER_PAGE. Ignore
56 // any subsequent custom pages.
57 if (!custom_page_views.empty()) {
58 custom_page_view_ = new CustomLauncherPageView(custom_page_views[0]);
60 AddLauncherPage(custom_page_view_,
61 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
64 // Start page.
65 start_page_view_ = new StartPageView(app_list_main_view_, view_delegate);
66 AddLauncherPage(start_page_view_, AppListModel::STATE_START);
69 // Search results UI.
70 search_results_page_view_ = new SearchResultPageView();
72 AppListModel::SearchResults* results = view_delegate->GetModel()->results();
73 search_results_page_view_->AddSearchResultContainerView(
74 results, new SearchResultListView(app_list_main_view_, view_delegate));
76 if (app_list::switches::IsExperimentalAppListEnabled()) {
77 search_results_page_view_->AddSearchResultContainerView(
78 results, new SearchResultTileItemListView(
79 GetSearchBoxView()->search_box(), view_delegate));
81 AddLauncherPage(search_results_page_view_,
82 AppListModel::STATE_SEARCH_RESULTS);
84 apps_container_view_ = new AppsContainerView(app_list_main_view_, model);
86 AddLauncherPage(apps_container_view_, AppListModel::STATE_APPS);
88 int initial_page_index = app_list::switches::IsExperimentalAppListEnabled()
89 ? GetPageIndexForState(AppListModel::STATE_START)
90 : GetPageIndexForState(AppListModel::STATE_APPS);
91 DCHECK_GE(initial_page_index, 0);
93 page_before_search_ = initial_page_index;
94 // Must only call SetTotalPages once all the launcher pages have been added
95 // (as it will trigger a SelectedPageChanged call).
96 pagination_model_.SetTotalPages(app_list_pages_.size());
98 // Page 0 is selected by SetTotalPages and needs to be 'hidden' when selecting
99 // the initial page.
100 app_list_pages_[GetActivePageIndex()]->OnWillBeHidden();
102 pagination_model_.SelectPage(initial_page_index, false);
104 ActivePageChanged();
107 void ContentsView::CancelDrag() {
108 if (apps_container_view_->apps_grid_view()->has_dragged_view())
109 apps_container_view_->apps_grid_view()->EndDrag(true);
110 if (apps_container_view_->app_list_folder_view()
111 ->items_grid_view()
112 ->has_dragged_view()) {
113 apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag(
114 true);
118 void ContentsView::SetDragAndDropHostOfCurrentAppList(
119 ApplicationDragAndDropHost* drag_and_drop_host) {
120 apps_container_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
123 void ContentsView::SetActiveState(AppListModel::State state) {
124 SetActiveState(state, true);
127 void ContentsView::SetActiveState(AppListModel::State state, bool animate) {
128 if (IsStateActive(state))
129 return;
131 SetActiveStateInternal(GetPageIndexForState(state), false, animate);
134 int ContentsView::GetActivePageIndex() const {
135 // The active page is changed at the beginning of an animation, not the end.
136 return pagination_model_.SelectedTargetPage();
139 AppListModel::State ContentsView::GetActiveState() const {
140 return GetStateForPageIndex(GetActivePageIndex());
143 bool ContentsView::IsStateActive(AppListModel::State state) const {
144 int active_page_index = GetActivePageIndex();
145 return active_page_index >= 0 &&
146 GetPageIndexForState(state) == active_page_index;
149 int ContentsView::GetPageIndexForState(AppListModel::State state) const {
150 // Find the index of the view corresponding to the given state.
151 std::map<AppListModel::State, int>::const_iterator it =
152 state_to_view_.find(state);
153 if (it == state_to_view_.end())
154 return -1;
156 return it->second;
159 AppListModel::State ContentsView::GetStateForPageIndex(int index) const {
160 std::map<int, AppListModel::State>::const_iterator it =
161 view_to_state_.find(index);
162 if (it == view_to_state_.end())
163 return AppListModel::INVALID_STATE;
165 return it->second;
168 int ContentsView::NumLauncherPages() const {
169 return pagination_model_.total_pages();
172 void ContentsView::SetActiveStateInternal(int page_index,
173 bool show_search_results,
174 bool animate) {
175 if (!GetPageView(page_index)->visible())
176 return;
178 if (!show_search_results)
179 page_before_search_ = page_index;
181 app_list_pages_[GetActivePageIndex()]->OnWillBeHidden();
183 // Start animating to the new page.
184 pagination_model_.SelectPage(page_index, animate);
185 ActivePageChanged();
187 if (!animate)
188 Layout();
191 void ContentsView::ActivePageChanged() {
192 AppListModel::State state = AppListModel::INVALID_STATE;
194 std::map<int, AppListModel::State>::const_iterator it =
195 view_to_state_.find(GetActivePageIndex());
196 if (it != view_to_state_.end())
197 state = it->second;
199 app_list_pages_[GetActivePageIndex()]->OnWillBeShown();
201 app_list_main_view_->model()->SetState(state);
203 if (switches::IsExperimentalAppListEnabled()) {
204 DCHECK(start_page_view_);
206 // Set the visibility of the search box's back button.
207 app_list_main_view_->search_box_view()->back_button()->SetVisible(
208 state != AppListModel::STATE_START);
209 app_list_main_view_->search_box_view()->Layout();
210 bool folder_active = (state == AppListModel::STATE_APPS)
211 ? apps_container_view_->IsInFolderView() : false;
212 app_list_main_view_->search_box_view()->SetBackButtonLabel(folder_active);
214 // Whenever the page changes, the custom launcher page is considered to have
215 // been reset.
216 app_list_main_view_->model()->ClearCustomLauncherPageSubpages();
219 app_list_main_view_->search_box_view()->ResetTabFocus(false);
222 void ContentsView::ShowSearchResults(bool show) {
223 int search_page = GetPageIndexForState(AppListModel::STATE_SEARCH_RESULTS);
224 DCHECK_GE(search_page, 0);
226 search_results_page_view_->ClearSelectedIndex();
228 SetActiveStateInternal(show ? search_page : page_before_search_, show, true);
231 bool ContentsView::IsShowingSearchResults() const {
232 return IsStateActive(AppListModel::STATE_SEARCH_RESULTS);
235 void ContentsView::NotifyCustomLauncherPageAnimationChanged(double progress,
236 int current_page,
237 int target_page) {
238 int custom_launcher_page_index =
239 GetPageIndexForState(AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
240 if (custom_launcher_page_index == target_page) {
241 app_list_main_view_->view_delegate()->CustomLauncherPageAnimationChanged(
242 progress);
243 } else if (custom_launcher_page_index == current_page) {
244 app_list_main_view_->view_delegate()->CustomLauncherPageAnimationChanged(
245 1 - progress);
249 void ContentsView::UpdatePageBounds() {
250 // The bounds calculations will potentially be mid-transition (depending on
251 // the state of the PaginationModel).
252 int current_page = std::max(0, pagination_model_.selected_page());
253 int target_page = current_page;
254 double progress = 1;
255 if (pagination_model_.has_transition()) {
256 const PaginationModel::Transition& transition =
257 pagination_model_.transition();
258 if (pagination_model_.is_valid_page(transition.target_page)) {
259 target_page = transition.target_page;
260 progress = transition.progress;
264 NotifyCustomLauncherPageAnimationChanged(progress, current_page, target_page);
266 AppListModel::State current_state = GetStateForPageIndex(current_page);
267 AppListModel::State target_state = GetStateForPageIndex(target_page);
269 // Update app list pages.
270 for (AppListPage* page : app_list_pages_) {
271 gfx::Rect to_rect = page->GetPageBoundsForState(target_state);
272 gfx::Rect from_rect = page->GetPageBoundsForState(current_state);
273 if (from_rect == to_rect)
274 continue;
276 // Animate linearly (the PaginationModel handles easing).
277 gfx::Rect bounds(
278 gfx::Tween::RectValueBetween(progress, from_rect, to_rect));
280 page->SetBoundsRect(bounds);
281 page->OnAnimationUpdated(progress, current_state, target_state);
284 // Update the search box.
285 UpdateSearchBox(progress, current_state, target_state);
288 void ContentsView::UpdateSearchBox(double progress,
289 AppListModel::State current_state,
290 AppListModel::State target_state) {
291 AppListPage* from_page = GetPageView(GetPageIndexForState(current_state));
292 AppListPage* to_page = GetPageView(GetPageIndexForState(target_state));
294 SearchBoxView* search_box = GetSearchBoxView();
296 gfx::Rect search_box_from(from_page->GetSearchBoxBounds());
297 gfx::Rect search_box_to(to_page->GetSearchBoxBounds());
298 gfx::Rect search_box_rect =
299 gfx::Tween::RectValueBetween(progress, search_box_from, search_box_to);
301 int original_z_height = from_page->GetSearchBoxZHeight();
302 int target_z_height = to_page->GetSearchBoxZHeight();
304 if (original_z_height != target_z_height) {
305 gfx::ShadowValue original_shadow = GetShadowForZHeight(original_z_height);
306 gfx::ShadowValue target_shadow = GetShadowForZHeight(target_z_height);
308 gfx::Vector2d offset(gfx::Tween::LinearIntValueBetween(
309 progress, original_shadow.x(), target_shadow.x()),
310 gfx::Tween::LinearIntValueBetween(
311 progress, original_shadow.y(), target_shadow.y()));
312 search_box->SetShadow(gfx::ShadowValue(
313 offset, gfx::Tween::LinearIntValueBetween(
314 progress, original_shadow.blur(), target_shadow.blur()),
315 gfx::Tween::ColorValueBetween(progress, original_shadow.color(),
316 target_shadow.color())));
318 search_box->GetWidget()->SetBounds(
319 search_box->GetViewBoundsForSearchBoxContentsBounds(
320 ConvertRectToWidget(search_box_rect)));
323 PaginationModel* ContentsView::GetAppsPaginationModel() {
324 return apps_container_view_->apps_grid_view()->pagination_model();
327 void ContentsView::ShowFolderContent(AppListFolderItem* item) {
328 apps_container_view_->ShowActiveFolder(item);
331 void ContentsView::Prerender() {
332 apps_container_view_->apps_grid_view()->Prerender();
335 AppListPage* ContentsView::GetPageView(int index) const {
336 DCHECK_GT(static_cast<int>(app_list_pages_.size()), index);
337 return app_list_pages_[index];
340 SearchBoxView* ContentsView::GetSearchBoxView() const {
341 return app_list_main_view_->search_box_view();
344 int ContentsView::AddLauncherPage(AppListPage* view) {
345 view->set_contents_view(this);
346 AddChildView(view);
347 app_list_pages_.push_back(view);
348 return app_list_pages_.size() - 1;
351 int ContentsView::AddLauncherPage(AppListPage* view,
352 AppListModel::State state) {
353 int page_index = AddLauncherPage(view);
354 bool success =
355 state_to_view_.insert(std::make_pair(state, page_index)).second;
356 success = success &&
357 view_to_state_.insert(std::make_pair(page_index, state)).second;
359 // There shouldn't be duplicates in either map.
360 DCHECK(success);
361 return page_index;
364 gfx::Rect ContentsView::GetDefaultSearchBoxBounds() const {
365 gfx::Rect search_box_bounds(0, 0, GetDefaultContentsSize().width(),
366 GetSearchBoxView()->GetPreferredSize().height());
367 if (switches::IsExperimentalAppListEnabled()) {
368 search_box_bounds.set_y(kExperimentalSearchBoxPadding);
369 search_box_bounds.Inset(kExperimentalSearchBoxPadding, 0);
371 return search_box_bounds;
374 gfx::Rect ContentsView::GetSearchBoxBoundsForState(
375 AppListModel::State state) const {
376 AppListPage* page = GetPageView(GetPageIndexForState(state));
377 return page->GetSearchBoxBounds();
380 gfx::Rect ContentsView::GetDefaultContentsBounds() const {
381 gfx::Rect bounds(gfx::Point(0, GetDefaultSearchBoxBounds().bottom()),
382 GetDefaultContentsSize());
383 return bounds;
386 bool ContentsView::Back() {
387 AppListModel::State state = view_to_state_[GetActivePageIndex()];
388 switch (state) {
389 case AppListModel::STATE_START:
390 // Close the app list when Back() is called from the start page.
391 return false;
392 case AppListModel::STATE_CUSTOM_LAUNCHER_PAGE:
393 if (app_list_main_view_->model()->PopCustomLauncherPageSubpage())
394 app_list_main_view_->view_delegate()->CustomLauncherPagePopSubpage();
395 else
396 SetActiveState(AppListModel::STATE_START);
397 break;
398 case AppListModel::STATE_APPS:
399 if (apps_container_view_->IsInFolderView())
400 apps_container_view_->app_list_folder_view()->CloseFolderPage();
401 else
402 SetActiveState(AppListModel::STATE_START);
403 break;
404 case AppListModel::STATE_SEARCH_RESULTS:
405 GetSearchBoxView()->ClearSearch();
406 ShowSearchResults(false);
407 break;
408 case AppListModel::INVALID_STATE: // Falls through.
409 NOTREACHED();
410 break;
412 return true;
415 gfx::Size ContentsView::GetDefaultContentsSize() const {
416 return apps_container_view_->apps_grid_view()->GetPreferredSize();
419 gfx::Size ContentsView::GetPreferredSize() const {
420 gfx::Rect search_box_bounds = GetDefaultSearchBoxBounds();
421 gfx::Rect default_contents_bounds = GetDefaultContentsBounds();
422 gfx::Vector2d bottom_right =
423 search_box_bounds.bottom_right().OffsetFromOrigin();
424 bottom_right.SetToMax(
425 default_contents_bounds.bottom_right().OffsetFromOrigin());
426 return gfx::Size(bottom_right.x(), bottom_right.y());
429 void ContentsView::Layout() {
430 // Immediately finish all current animations.
431 pagination_model_.FinishAnimation();
433 double progress =
434 IsStateActive(AppListModel::STATE_CUSTOM_LAUNCHER_PAGE) ? 1 : 0;
436 // Notify the custom launcher page that the active page has changed.
437 app_list_main_view_->view_delegate()->CustomLauncherPageAnimationChanged(
438 progress);
440 if (GetContentsBounds().IsEmpty())
441 return;
443 for (AppListPage* page : app_list_pages_) {
444 page->SetBoundsRect(page->GetPageBoundsForState(GetActiveState()));
447 // The search box is contained in a widget so set the bounds of the widget
448 // rather than the SearchBoxView.
449 views::Widget* search_box_widget = GetSearchBoxView()->GetWidget();
450 if (search_box_widget && search_box_widget != GetWidget()) {
451 gfx::Rect search_box_bounds = GetSearchBoxBoundsForState(GetActiveState());
452 search_box_widget->SetBounds(ConvertRectToWidget(
453 GetSearchBoxView()->GetViewBoundsForSearchBoxContentsBounds(
454 search_box_bounds)));
458 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
459 bool handled = app_list_pages_[GetActivePageIndex()]->OnKeyPressed(event);
461 if (!handled) {
462 if (event.key_code() == ui::VKEY_TAB && event.IsShiftDown()) {
463 GetSearchBoxView()->MoveTabFocus(true);
464 handled = true;
468 return handled;
471 void ContentsView::TotalPagesChanged() {
474 void ContentsView::SelectedPageChanged(int old_selected, int new_selected) {
475 if (old_selected >= 0)
476 app_list_pages_[old_selected]->OnHidden();
478 if (new_selected >= 0)
479 app_list_pages_[new_selected]->OnShown();
482 void ContentsView::TransitionStarted() {
485 void ContentsView::TransitionChanged() {
486 UpdatePageBounds();
489 } // namespace app_list