ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / ui / app_list / views / contents_view.cc
blob3c27e17261c5c1f58c6b8b8fa7777941c1198b14
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/contents_animator.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/view_model_utils.h"
28 #include "ui/views/widget/widget.h"
30 namespace app_list {
32 ContentsView::ContentsView(AppListMainView* app_list_main_view)
33 : apps_container_view_(nullptr),
34 search_results_page_view_(nullptr),
35 start_page_view_(nullptr),
36 custom_page_view_(nullptr),
37 app_list_main_view_(app_list_main_view),
38 view_model_(new views::ViewModel),
39 page_before_search_(0) {
40 pagination_model_.SetTransitionDurations(kPageTransitionDurationInMs,
41 kOverscrollPageTransitionDurationMs);
42 pagination_model_.AddObserver(this);
45 ContentsView::~ContentsView() {
46 pagination_model_.RemoveObserver(this);
49 void ContentsView::Init(AppListModel* model) {
50 DCHECK(model);
52 AppListViewDelegate* view_delegate = app_list_main_view_->view_delegate();
54 if (app_list::switches::IsExperimentalAppListEnabled()) {
55 std::vector<views::View*> custom_page_views =
56 view_delegate->CreateCustomPageWebViews(GetLocalBounds().size());
57 for (std::vector<views::View*>::const_iterator it =
58 custom_page_views.begin();
59 it != custom_page_views.end();
60 ++it) {
61 // Only the first launcher page is considered to represent
62 // STATE_CUSTOM_LAUNCHER_PAGE.
63 if (it == custom_page_views.begin()) {
64 custom_page_view_ = *it;
66 AddLauncherPage(*it, AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
67 } else {
68 AddLauncherPage(*it);
72 // Start page.
73 start_page_view_ = new StartPageView(app_list_main_view_, view_delegate);
74 AddLauncherPage(start_page_view_, AppListModel::STATE_START);
77 // Search results UI.
78 search_results_page_view_ = new SearchResultPageView();
80 AppListModel::SearchResults* results = view_delegate->GetModel()->results();
81 search_results_page_view_->AddSearchResultContainerView(
82 results, new SearchResultListView(app_list_main_view_, view_delegate));
84 if (app_list::switches::IsExperimentalAppListEnabled()) {
85 search_results_page_view_->AddSearchResultContainerView(
86 results,
87 new SearchResultTileItemListView(GetSearchBoxView()->search_box()));
89 AddLauncherPage(search_results_page_view_,
90 AppListModel::STATE_SEARCH_RESULTS);
92 apps_container_view_ = new AppsContainerView(app_list_main_view_, model);
94 AddLauncherPage(apps_container_view_, AppListModel::STATE_APPS);
96 int initial_page_index = app_list::switches::IsExperimentalAppListEnabled()
97 ? GetPageIndexForState(AppListModel::STATE_START)
98 : GetPageIndexForState(AppListModel::STATE_APPS);
99 DCHECK_GE(initial_page_index, 0);
101 page_before_search_ = initial_page_index;
102 // Must only call SetTotalPages once all the launcher pages have been added
103 // (as it will trigger a SelectedPageChanged call).
104 pagination_model_.SetTotalPages(view_model_->view_size());
105 pagination_model_.SelectPage(initial_page_index, false);
107 ActivePageChanged();
109 // Populate the contents animators.
110 AddAnimator(AppListModel::STATE_START, AppListModel::STATE_APPS,
111 scoped_ptr<ContentsAnimator>(new StartToAppsAnimator(this)));
112 AddAnimator(AppListModel::STATE_START,
113 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE,
114 scoped_ptr<ContentsAnimator>(new StartToCustomAnimator(this)));
115 default_animator_.reset(new DefaultAnimator(this));
118 void ContentsView::CancelDrag() {
119 if (apps_container_view_->apps_grid_view()->has_dragged_view())
120 apps_container_view_->apps_grid_view()->EndDrag(true);
121 if (apps_container_view_->app_list_folder_view()
122 ->items_grid_view()
123 ->has_dragged_view()) {
124 apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag(
125 true);
129 void ContentsView::SetDragAndDropHostOfCurrentAppList(
130 ApplicationDragAndDropHost* drag_and_drop_host) {
131 apps_container_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
134 void ContentsView::SetActivePage(int page_index) {
135 SetActivePage(page_index, true);
138 void ContentsView::SetActivePage(int page_index, bool animate) {
139 if (GetActivePageIndex() == page_index)
140 return;
142 SetActivePageInternal(page_index, false, animate);
145 int ContentsView::GetActivePageIndex() const {
146 // The active page is changed at the beginning of an animation, not the end.
147 return pagination_model_.SelectedTargetPage();
150 AppListModel::State ContentsView::GetActiveState() const {
151 return GetStateForPageIndex(GetActivePageIndex());
154 bool ContentsView::IsStateActive(AppListModel::State state) const {
155 int active_page_index = GetActivePageIndex();
156 return active_page_index >= 0 &&
157 GetPageIndexForState(state) == active_page_index;
160 int ContentsView::GetPageIndexForState(AppListModel::State state) const {
161 // Find the index of the view corresponding to the given state.
162 std::map<AppListModel::State, int>::const_iterator it =
163 state_to_view_.find(state);
164 if (it == state_to_view_.end())
165 return -1;
167 return it->second;
170 AppListModel::State ContentsView::GetStateForPageIndex(int index) const {
171 std::map<int, AppListModel::State>::const_iterator it =
172 view_to_state_.find(index);
173 if (it == view_to_state_.end())
174 return AppListModel::INVALID_STATE;
176 return it->second;
179 int ContentsView::NumLauncherPages() const {
180 return pagination_model_.total_pages();
183 void ContentsView::SetActivePageInternal(int page_index,
184 bool show_search_results,
185 bool animate) {
186 if (!GetPageView(page_index)->visible())
187 return;
189 if (!show_search_results)
190 page_before_search_ = page_index;
191 // Start animating to the new page.
192 pagination_model_.SelectPage(page_index, animate);
193 ActivePageChanged();
195 if (!animate)
196 Layout();
199 void ContentsView::ActivePageChanged() {
200 AppListModel::State state = AppListModel::INVALID_STATE;
202 // TODO(calamity): This does not report search results being shown in the
203 // experimental app list as a boolean is currently used to indicate whether
204 // search results are showing. See http://crbug.com/427787/.
205 std::map<int, AppListModel::State>::const_iterator it =
206 view_to_state_.find(pagination_model_.SelectedTargetPage());
207 if (it != view_to_state_.end())
208 state = it->second;
210 app_list_main_view_->model()->SetState(state);
212 if (switches::IsExperimentalAppListEnabled()) {
213 DCHECK(start_page_view_);
215 // Set the visibility of the search box's back button.
216 app_list_main_view_->search_box_view()->back_button()->SetVisible(
217 state != AppListModel::STATE_START);
218 app_list_main_view_->search_box_view()->Layout();
220 // Whenever the page changes, the custom launcher page is considered to have
221 // been reset.
222 app_list_main_view_->model()->ClearCustomLauncherPageSubpages();
225 app_list_main_view_->search_box_view()->ResetTabFocus(false);
226 apps_container_view_->apps_grid_view()->ClearAnySelectedView();
227 apps_container_view_->app_list_folder_view()->items_grid_view()
228 ->ClearAnySelectedView();
230 if (custom_page_view_) {
231 custom_page_view_->SetFocusable(state ==
232 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
236 void ContentsView::ShowSearchResults(bool show) {
237 int search_page = GetPageIndexForState(AppListModel::STATE_SEARCH_RESULTS);
238 DCHECK_GE(search_page, 0);
240 SetActivePageInternal(show ? search_page : page_before_search_, show, true);
243 bool ContentsView::IsShowingSearchResults() const {
244 return IsStateActive(AppListModel::STATE_SEARCH_RESULTS);
247 void ContentsView::NotifyCustomLauncherPageAnimationChanged(double progress,
248 int current_page,
249 int target_page) {
250 int custom_launcher_page_index =
251 GetPageIndexForState(AppListModel::STATE_CUSTOM_LAUNCHER_PAGE);
252 if (custom_launcher_page_index == target_page) {
253 app_list_main_view_->view_delegate()->CustomLauncherPageAnimationChanged(
254 progress);
255 } else if (custom_launcher_page_index == current_page) {
256 app_list_main_view_->view_delegate()->CustomLauncherPageAnimationChanged(
257 1 - progress);
261 void ContentsView::UpdatePageBounds() {
262 // The bounds calculations will potentially be mid-transition (depending on
263 // the state of the PaginationModel).
264 int current_page = std::max(0, pagination_model_.selected_page());
265 int target_page = current_page;
266 double progress = 1;
267 if (pagination_model_.has_transition()) {
268 const PaginationModel::Transition& transition =
269 pagination_model_.transition();
270 if (pagination_model_.is_valid_page(transition.target_page)) {
271 target_page = transition.target_page;
272 progress = transition.progress;
276 NotifyCustomLauncherPageAnimationChanged(progress, current_page, target_page);
278 bool reverse;
279 ContentsAnimator* animator =
280 GetAnimatorForTransition(current_page, target_page, &reverse);
282 // Animate linearly (the PaginationModel handles easing).
283 if (reverse)
284 animator->Update(1 - progress, target_page, current_page);
285 else
286 animator->Update(progress, current_page, target_page);
289 PaginationModel* ContentsView::GetAppsPaginationModel() {
290 return apps_container_view_->apps_grid_view()->pagination_model();
293 void ContentsView::AddAnimator(AppListModel::State from_state,
294 AppListModel::State to_state,
295 scoped_ptr<ContentsAnimator> animator) {
296 int from_page = GetPageIndexForState(from_state);
297 int to_page = GetPageIndexForState(to_state);
298 contents_animators_.insert(
299 std::make_pair(std::make_pair(from_page, to_page),
300 linked_ptr<ContentsAnimator>(animator.release())));
303 ContentsAnimator* ContentsView::GetAnimatorForTransition(int from_page,
304 int to_page,
305 bool* reverse) const {
306 auto it = contents_animators_.find(std::make_pair(from_page, to_page));
307 if (it != contents_animators_.end()) {
308 *reverse = false;
309 return it->second.get();
312 it = contents_animators_.find(std::make_pair(to_page, from_page));
313 if (it != contents_animators_.end()) {
314 *reverse = true;
315 return it->second.get();
318 *reverse = false;
319 return default_animator_.get();
322 void ContentsView::ShowFolderContent(AppListFolderItem* item) {
323 apps_container_view_->ShowActiveFolder(item);
326 void ContentsView::Prerender() {
327 apps_container_view_->apps_grid_view()->Prerender();
330 views::View* ContentsView::GetPageView(int index) const {
331 return view_model_->view_at(index);
334 SearchBoxView* ContentsView::GetSearchBoxView() const {
335 return app_list_main_view_->search_box_view();
338 void ContentsView::AddBlankPageForTesting() {
339 AddLauncherPage(new views::View);
340 pagination_model_.SetTotalPages(view_model_->view_size());
343 int ContentsView::AddLauncherPage(views::View* view) {
344 int page_index = view_model_->view_size();
345 AddChildView(view);
346 view_model_->Add(view, page_index);
347 return page_index;
350 int ContentsView::AddLauncherPage(views::View* view,
351 AppListModel::State state) {
352 int page_index = AddLauncherPage(view);
353 bool success =
354 state_to_view_.insert(std::make_pair(state, page_index)).second;
355 success = success &&
356 view_to_state_.insert(std::make_pair(page_index, state)).second;
358 // There shouldn't be duplicates in either map.
359 DCHECK(success);
360 return page_index;
363 gfx::Rect ContentsView::GetOnscreenPageBounds(int page_index) const {
364 AppListModel::State state = GetStateForPageIndex(page_index);
365 bool fills_contents_view =
366 state == AppListModel::STATE_CUSTOM_LAUNCHER_PAGE ||
367 state == AppListModel::STATE_START;
368 return fills_contents_view ? GetContentsBounds() : GetDefaultContentsBounds();
371 gfx::Rect ContentsView::GetOffscreenPageBounds(int page_index) const {
372 AppListModel::State state = GetStateForPageIndex(page_index);
373 gfx::Rect bounds(GetOnscreenPageBounds(page_index));
374 // The start page and search page origins are above; all other pages' origins
375 // are below.
376 bool origin_above = state == AppListModel::STATE_START ||
377 state == AppListModel::STATE_SEARCH_RESULTS;
378 bounds.set_y(origin_above ? -bounds.height()
379 : GetContentsBounds().height() + bounds.y());
380 return bounds;
383 gfx::Rect ContentsView::GetDefaultSearchBoxBounds() const {
384 gfx::Rect search_box_bounds(0, 0, GetDefaultContentsSize().width(),
385 GetSearchBoxView()->GetPreferredSize().height());
386 if (switches::IsExperimentalAppListEnabled()) {
387 search_box_bounds.set_y(kExperimentalSearchBoxPadding);
388 search_box_bounds.Inset(kExperimentalSearchBoxPadding, 0);
390 return search_box_bounds;
393 gfx::Rect ContentsView::GetSearchBoxBoundsForState(
394 AppListModel::State state) const {
395 // On the start page, the search box is in a different location.
396 if (state == AppListModel::STATE_START) {
397 DCHECK(start_page_view_);
398 // Convert to ContentsView space, assuming that the StartPageView is in the
399 // ContentsView's default bounds.
400 return start_page_view_->GetSearchBoxBounds() +
401 GetOnscreenPageBounds(GetPageIndexForState(state))
402 .OffsetFromOrigin();
405 return GetDefaultSearchBoxBounds();
408 gfx::Rect ContentsView::GetSearchBoxBoundsForPageIndex(int index) const {
409 return GetSearchBoxBoundsForState(GetStateForPageIndex(index));
412 gfx::Rect ContentsView::GetDefaultContentsBounds() const {
413 gfx::Rect bounds(gfx::Point(0, GetDefaultSearchBoxBounds().bottom()),
414 GetDefaultContentsSize());
415 return bounds;
418 gfx::Rect ContentsView::GetCustomPageCollapsedBounds() const {
419 gfx::Rect bounds(GetContentsBounds());
420 int page_height = bounds.height();
421 bounds.set_y(page_height - kCustomPageCollapsedHeight);
422 return bounds;
425 bool ContentsView::Back() {
426 AppListModel::State state = view_to_state_[GetActivePageIndex()];
427 switch (state) {
428 case AppListModel::STATE_START:
429 // Close the app list when Back() is called from the start page.
430 return false;
431 case AppListModel::STATE_CUSTOM_LAUNCHER_PAGE:
432 if (app_list_main_view_->model()->PopCustomLauncherPageSubpage())
433 app_list_main_view_->view_delegate()->CustomLauncherPagePopSubpage();
434 else
435 SetActivePage(GetPageIndexForState(AppListModel::STATE_START));
436 break;
437 case AppListModel::STATE_APPS:
438 if (apps_container_view_->IsInFolderView())
439 apps_container_view_->app_list_folder_view()->CloseFolderPage();
440 else
441 SetActivePage(GetPageIndexForState(AppListModel::STATE_START));
442 break;
443 case AppListModel::STATE_SEARCH_RESULTS:
444 GetSearchBoxView()->ClearSearch();
445 ShowSearchResults(false);
446 break;
447 case AppListModel::INVALID_STATE: // Falls through.
448 NOTREACHED();
449 break;
451 return true;
454 gfx::Size ContentsView::GetDefaultContentsSize() const {
455 return apps_container_view_->apps_grid_view()->GetPreferredSize();
458 gfx::Size ContentsView::GetPreferredSize() const {
459 gfx::Rect search_box_bounds = GetDefaultSearchBoxBounds();
460 gfx::Rect default_contents_bounds = GetDefaultContentsBounds();
461 gfx::Vector2d bottom_right =
462 search_box_bounds.bottom_right().OffsetFromOrigin();
463 bottom_right.SetToMax(
464 default_contents_bounds.bottom_right().OffsetFromOrigin());
465 return gfx::Size(bottom_right.x(), bottom_right.y());
468 void ContentsView::Layout() {
469 // Immediately finish all current animations.
470 pagination_model_.FinishAnimation();
472 // Move the current view onto the screen, and all other views off screen to
473 // the left. (Since we are not animating, we don't need to be careful about
474 // which side we place the off-screen views onto.)
475 gfx::Rect rect = GetOnscreenPageBounds(GetActivePageIndex());
476 double progress =
477 IsStateActive(AppListModel::STATE_CUSTOM_LAUNCHER_PAGE) ? 1 : 0;
479 // Notify the custom launcher page that the active page has changed.
480 app_list_main_view_->view_delegate()->CustomLauncherPageAnimationChanged(
481 progress);
483 if (rect.IsEmpty())
484 return;
486 gfx::Rect offscreen_target(rect);
487 offscreen_target.set_x(-rect.width());
489 int current_page = GetActivePageIndex();
491 for (int i = 0; i < view_model_->view_size(); ++i) {
492 view_model_->view_at(i)
493 ->SetBoundsRect(i == current_page ? rect : offscreen_target);
496 // Custom locations of pages in certain states.
497 // Within the start page, the custom page is given its collapsed bounds.
498 int start_page_index = GetPageIndexForState(AppListModel::STATE_START);
499 if (current_page == start_page_index) {
500 if (custom_page_view_)
501 custom_page_view_->SetBoundsRect(GetCustomPageCollapsedBounds());
504 // The search box is contained in a widget so set the bounds of the widget
505 // rather than the SearchBoxView. In athena, the search box widget will be the
506 // same as the app list widget so don't move it.
507 views::Widget* search_box_widget = GetSearchBoxView()->GetWidget();
508 if (search_box_widget && search_box_widget != GetWidget()) {
509 gfx::Rect search_box_bounds = GetSearchBoxBoundsForState(GetActiveState());
510 search_box_widget->SetBounds(ConvertRectToWidget(
511 GetSearchBoxView()->GetViewBoundsForSearchBoxContentsBounds(
512 search_box_bounds)));
516 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
517 bool handled =
518 view_model_->view_at(GetActivePageIndex())->OnKeyPressed(event);
520 if (!handled) {
521 if (event.key_code() == ui::VKEY_TAB && event.IsShiftDown()) {
522 GetSearchBoxView()->MoveTabFocus(true);
523 handled = true;
527 return handled;
530 void ContentsView::TotalPagesChanged() {
533 void ContentsView::SelectedPageChanged(int old_selected, int new_selected) {
534 // TODO(mgiuca): This should be generalized so we call a virtual OnShow and
535 // OnHide method for each page.
536 if (!start_page_view_)
537 return;
539 if (new_selected == GetPageIndexForState(AppListModel::STATE_START)) {
540 start_page_view_->OnShow();
541 // Show or hide the custom page view, based on whether it is enabled.
542 if (custom_page_view_) {
543 custom_page_view_->SetVisible(
544 app_list_main_view_->ShouldShowCustomLauncherPage());
549 void ContentsView::TransitionStarted() {
552 void ContentsView::TransitionChanged() {
553 UpdatePageBounds();
556 } // namespace app_list