| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/app_list/views/contents_view.h" |
| |
| #include <algorithm> |
| |
| #include "base/logging.h" |
| #include "ui/app_list/app_list_constants.h" |
| #include "ui/app_list/app_list_switches.h" |
| #include "ui/app_list/app_list_view_delegate.h" |
| #include "ui/app_list/pagination_model.h" |
| #include "ui/app_list/views/app_list_folder_view.h" |
| #include "ui/app_list/views/app_list_main_view.h" |
| #include "ui/app_list/views/apps_container_view.h" |
| #include "ui/app_list/views/apps_grid_view.h" |
| #include "ui/app_list/views/search_result_list_view.h" |
| #include "ui/app_list/views/start_page_view.h" |
| #include "ui/events/event.h" |
| #include "ui/views/animation/bounds_animator.h" |
| #include "ui/views/view_model.h" |
| #include "ui/views/view_model_utils.h" |
| |
| namespace app_list { |
| |
| namespace { |
| |
| // Indexes of interesting views in ViewModel of ContentsView. |
| const int kIndexAppsContainer = 0; |
| const int kIndexSearchResults = 1; |
| const int kIndexStartPage = 2; |
| |
| const int kMinMouseWheelToSwitchPage = 20; |
| const int kMinScrollToSwitchPage = 20; |
| const int kMinHorizVelocityToSwitchPage = 800; |
| |
| const double kFinishTransitionThreshold = 0.33; |
| |
| AppsContainerView* GetAppsContainerView(views::ViewModel* model) { |
| return static_cast<AppsContainerView*>(model->view_at(kIndexAppsContainer)); |
| } |
| |
| SearchResultListView* GetSearchResultListView(views::ViewModel* model) { |
| return static_cast<SearchResultListView*>( |
| model->view_at(kIndexSearchResults)); |
| } |
| |
| StartPageView* GetStartPageView(views::ViewModel* model) { |
| return static_cast<StartPageView*>(model->view_at(kIndexStartPage)); |
| } |
| |
| } // namespace |
| |
| ContentsView::ContentsView(AppListMainView* app_list_main_view, |
| PaginationModel* pagination_model, |
| AppListModel* model, |
| AppListViewDelegate* view_delegate) |
| : show_state_(SHOW_APPS), |
| pagination_model_(pagination_model), |
| start_page_view_(NULL), |
| app_list_main_view_(app_list_main_view), |
| view_model_(new views::ViewModel), |
| bounds_animator_(new views::BoundsAnimator(this)) { |
| DCHECK(model); |
| pagination_model_->SetTransitionDurations( |
| kPageTransitionDurationInMs, |
| kOverscrollPageTransitionDurationMs); |
| |
| apps_container_view_ = |
| new AppsContainerView(app_list_main_view, pagination_model, model); |
| AddChildView(apps_container_view_); |
| view_model_->Add(apps_container_view_, kIndexAppsContainer); |
| |
| SearchResultListView* search_results_view = new SearchResultListView( |
| app_list_main_view, view_delegate); |
| AddChildView(search_results_view); |
| view_model_->Add(search_results_view, kIndexSearchResults); |
| |
| if (app_list::switches::IsExperimentalAppListEnabled()) { |
| start_page_view_ = new StartPageView(app_list_main_view, view_delegate); |
| AddChildView(start_page_view_); |
| view_model_->Add(start_page_view_, kIndexStartPage); |
| } |
| |
| GetSearchResultListView(view_model_.get())->SetResults(model->results()); |
| } |
| |
| ContentsView::~ContentsView() { |
| } |
| |
| void ContentsView::CancelDrag() { |
| if (apps_container_view_->apps_grid_view()->has_dragged_view()) |
| apps_container_view_->apps_grid_view()->EndDrag(true); |
| if (apps_container_view_->app_list_folder_view() |
| ->items_grid_view() |
| ->has_dragged_view()) { |
| apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag( |
| true); |
| } |
| } |
| |
| void ContentsView::SetDragAndDropHostOfCurrentAppList( |
| ApplicationDragAndDropHost* drag_and_drop_host) { |
| apps_container_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host); |
| } |
| |
| void ContentsView::SetShowState(ShowState show_state) { |
| if (show_state_ == show_state) |
| return; |
| |
| show_state_ = show_state; |
| ShowStateChanged(); |
| } |
| |
| void ContentsView::ShowStateChanged() { |
| SearchResultListView* results_view = |
| GetSearchResultListView(view_model_.get()); |
| // TODO(xiyuan): Highlight default match instead of the first. |
| if (show_state_ == SHOW_SEARCH_RESULTS && results_view->visible()) |
| results_view->SetSelectedIndex(0); |
| results_view->UpdateAutoLaunchState(); |
| |
| // Notify parent AppListMainView of show state change. |
| app_list_main_view_->OnContentsViewShowStateChanged(); |
| |
| if (show_state_ == SHOW_START_PAGE) |
| GetStartPageView(view_model_.get())->Reset(); |
| |
| AnimateToIdealBounds(); |
| } |
| |
| void ContentsView::CalculateIdealBounds() { |
| gfx::Rect rect(GetContentsBounds()); |
| if (rect.IsEmpty()) |
| return; |
| |
| if (app_list::switches::IsExperimentalAppListEnabled()) { |
| int incoming_view_index = 0; |
| switch (show_state_) { |
| case SHOW_APPS: |
| incoming_view_index = kIndexAppsContainer; |
| break; |
| case SHOW_SEARCH_RESULTS: |
| incoming_view_index = kIndexSearchResults; |
| break; |
| case SHOW_START_PAGE: |
| incoming_view_index = kIndexStartPage; |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| gfx::Rect incoming_target(rect); |
| gfx::Rect outgoing_target(rect); |
| outgoing_target.set_x(-outgoing_target.width()); |
| |
| for (int i = 0; i < view_model_->view_size(); ++i) { |
| view_model_->set_ideal_bounds(i, |
| i == incoming_view_index ? incoming_target |
| : outgoing_target); |
| } |
| return; |
| } |
| |
| gfx::Rect container_frame(rect); |
| gfx::Rect results_frame(rect); |
| |
| // Offsets apps grid and result list based on |show_state_|. |
| // SearchResultListView is on top of apps grid. Visible view is left in |
| // visible area and invisible ones is put out of the visible area. |
| int contents_area_height = rect.height(); |
| switch (show_state_) { |
| case SHOW_APPS: |
| results_frame.Offset(0, -contents_area_height); |
| break; |
| case SHOW_SEARCH_RESULTS: |
| container_frame.Offset(0, contents_area_height); |
| break; |
| default: |
| NOTREACHED() << "Unknown show_state_ " << show_state_; |
| break; |
| } |
| |
| view_model_->set_ideal_bounds(kIndexAppsContainer, container_frame); |
| view_model_->set_ideal_bounds(kIndexSearchResults, results_frame); |
| } |
| |
| void ContentsView::AnimateToIdealBounds() { |
| CalculateIdealBounds(); |
| for (int i = 0; i < view_model_->view_size(); ++i) { |
| bounds_animator_->AnimateViewTo(view_model_->view_at(i), |
| view_model_->ideal_bounds(i)); |
| } |
| } |
| |
| void ContentsView::ShowSearchResults(bool show) { |
| SetShowState(show ? SHOW_SEARCH_RESULTS : SHOW_APPS); |
| } |
| |
| void ContentsView::ShowFolderContent(AppListFolderItem* item) { |
| apps_container_view_->ShowActiveFolder(item); |
| } |
| |
| void ContentsView::Prerender() { |
| const int selected_page = std::max(0, pagination_model_->selected_page()); |
| apps_container_view_->apps_grid_view()->Prerender(selected_page); |
| } |
| |
| gfx::Size ContentsView::GetPreferredSize() const { |
| const gfx::Size container_size = GetAppsContainerView(view_model_.get())-> |
| apps_grid_view()->GetPreferredSize(); |
| const gfx::Size results_size = |
| GetSearchResultListView(view_model_.get())->GetPreferredSize(); |
| |
| int width = std::max(container_size.width(), results_size.width()); |
| int height = std::max(container_size.height(), results_size.height()); |
| return gfx::Size(width, height); |
| } |
| |
| void ContentsView::Layout() { |
| CalculateIdealBounds(); |
| views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_); |
| } |
| |
| bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) { |
| switch (show_state_) { |
| case SHOW_APPS: |
| return GetAppsContainerView(view_model_.get())->OnKeyPressed(event); |
| case SHOW_SEARCH_RESULTS: |
| return GetSearchResultListView(view_model_.get())->OnKeyPressed(event); |
| case SHOW_START_PAGE: |
| return GetStartPageView(view_model_.get())->OnKeyPressed(event); |
| default: |
| NOTREACHED() << "Unknown show state " << show_state_; |
| } |
| return false; |
| } |
| |
| bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) { |
| if (show_state_ != SHOW_APPS) |
| return false; |
| |
| int offset; |
| if (abs(event.x_offset()) > abs(event.y_offset())) |
| offset = event.x_offset(); |
| else |
| offset = event.y_offset(); |
| |
| if (abs(offset) > kMinMouseWheelToSwitchPage) { |
| if (!pagination_model_->has_transition()) { |
| pagination_model_->SelectPageRelative( |
| offset > 0 ? -1 : 1, true); |
| } |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void ContentsView::OnGestureEvent(ui::GestureEvent* event) { |
| if (show_state_ != SHOW_APPS) |
| return; |
| |
| switch (event->type()) { |
| case ui::ET_GESTURE_SCROLL_BEGIN: |
| pagination_model_->StartScroll(); |
| event->SetHandled(); |
| return; |
| case ui::ET_GESTURE_SCROLL_UPDATE: |
| // event->details.scroll_x() > 0 means moving contents to right. That is, |
| // transitioning to previous page. |
| pagination_model_->UpdateScroll( |
| event->details().scroll_x() / GetContentsBounds().width()); |
| event->SetHandled(); |
| return; |
| case ui::ET_GESTURE_SCROLL_END: |
| pagination_model_->EndScroll(pagination_model_-> |
| transition().progress < kFinishTransitionThreshold); |
| event->SetHandled(); |
| return; |
| case ui::ET_SCROLL_FLING_START: { |
| pagination_model_->EndScroll(true); |
| if (fabs(event->details().velocity_x()) > kMinHorizVelocityToSwitchPage) { |
| pagination_model_->SelectPageRelative( |
| event->details().velocity_x() < 0 ? 1 : -1, |
| true); |
| } |
| event->SetHandled(); |
| return; |
| } |
| default: |
| break; |
| } |
| } |
| |
| void ContentsView::OnScrollEvent(ui::ScrollEvent* event) { |
| if (show_state_ != SHOW_APPS || |
| event->type() == ui::ET_SCROLL_FLING_CANCEL) { |
| return; |
| } |
| |
| float offset; |
| if (std::abs(event->x_offset()) > std::abs(event->y_offset())) |
| offset = event->x_offset(); |
| else |
| offset = event->y_offset(); |
| |
| if (std::abs(offset) > kMinScrollToSwitchPage) { |
| if (!pagination_model_->has_transition()) { |
| pagination_model_->SelectPageRelative(offset > 0 ? -1 : 1, |
| true); |
| } |
| event->SetHandled(); |
| event->StopPropagation(); |
| } |
| } |
| |
| } // namespace app_list |