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 "ui/app_list/app_list_constants.h" 11#include "ui/app_list/app_list_view_delegate.h" 12#include "ui/app_list/pagination_model.h" 13#include "ui/app_list/views/app_list_main_view.h" 14#include "ui/app_list/views/apps_container_view.h" 15#include "ui/app_list/views/apps_grid_view.h" 16#include "ui/app_list/views/search_result_list_view.h" 17#include "ui/events/event.h" 18#include "ui/views/animation/bounds_animator.h" 19#include "ui/views/view_model.h" 20#include "ui/views/view_model_utils.h" 21 22namespace app_list { 23 24namespace { 25 26// Indexes of interesting views in ViewModel of ContentsView. 27const int kIndexAppsContainer = 0; 28const int kIndexSearchResults = 1; 29 30const int kMinMouseWheelToSwitchPage = 20; 31const int kMinScrollToSwitchPage = 20; 32const int kMinHorizVelocityToSwitchPage = 800; 33 34const double kFinishTransitionThreshold = 0.33; 35 36AppsContainerView* GetAppsContainerView(views::ViewModel* model) { 37 return static_cast<AppsContainerView*>(model->view_at(kIndexAppsContainer)); 38} 39 40SearchResultListView* GetSearchResultListView(views::ViewModel* model) { 41 return static_cast<SearchResultListView*>( 42 model->view_at(kIndexSearchResults)); 43} 44 45} // namespace 46 47ContentsView::ContentsView(AppListMainView* app_list_main_view, 48 PaginationModel* pagination_model, 49 AppListModel* model, 50 content::WebContents* start_page_contents) 51 : show_state_(SHOW_APPS), 52 pagination_model_(pagination_model), 53 view_model_(new views::ViewModel), 54 bounds_animator_(new views::BoundsAnimator(this)) { 55 DCHECK(model); 56 pagination_model_->SetTransitionDurations( 57 kPageTransitionDurationInMs, 58 kOverscrollPageTransitionDurationMs); 59 60 apps_container_view_ = new AppsContainerView( 61 app_list_main_view, pagination_model, model, start_page_contents); 62 AddChildView(apps_container_view_); 63 view_model_->Add(apps_container_view_, kIndexAppsContainer); 64 65 SearchResultListView* search_results_view = new SearchResultListView( 66 app_list_main_view); 67 AddChildView(search_results_view); 68 view_model_->Add(search_results_view, kIndexSearchResults); 69 70 GetSearchResultListView(view_model_.get())->SetResults(model->results()); 71} 72 73ContentsView::~ContentsView() { 74} 75 76void ContentsView::CancelDrag() { 77 if (apps_container_view_->apps_grid_view()->has_dragged_view()) 78 apps_container_view_->apps_grid_view()->EndDrag(true); 79} 80 81void ContentsView::SetDragAndDropHostOfCurrentAppList( 82 ApplicationDragAndDropHost* drag_and_drop_host) { 83 apps_container_view_->apps_grid_view()-> 84 SetDragAndDropHostOfCurrentAppList(drag_and_drop_host); 85} 86 87void ContentsView::SetShowState(ShowState show_state) { 88 if (show_state_ == show_state) 89 return; 90 91 show_state_ = show_state; 92 ShowStateChanged(); 93} 94 95void ContentsView::ShowStateChanged() { 96 if (show_state_ == SHOW_SEARCH_RESULTS) { 97 // TODO(xiyuan): Highlight default match instead of the first. 98 SearchResultListView* results_view = 99 GetSearchResultListView(view_model_.get()); 100 if (results_view->visible()) 101 results_view->SetSelectedIndex(0); 102 } 103 104 AnimateToIdealBounds(); 105} 106 107void ContentsView::CalculateIdealBounds() { 108 gfx::Rect rect(GetContentsBounds()); 109 if (rect.IsEmpty()) 110 return; 111 112 gfx::Rect container_frame(rect); 113 gfx::Rect results_frame(rect); 114 115 // Offsets apps grid and result list based on |show_state_|. 116 // SearchResultListView is on top of apps grid. Visible view is left in 117 // visible area and invisible ones is put out of the visible area. 118 int contents_area_height = rect.height(); 119 switch (show_state_) { 120 case SHOW_APPS: 121 results_frame.Offset(0, -contents_area_height); 122 break; 123 case SHOW_SEARCH_RESULTS: 124 container_frame.Offset(0, contents_area_height); 125 break; 126 default: 127 NOTREACHED() << "Unknown show_state_ " << show_state_; 128 break; 129 } 130 131 view_model_->set_ideal_bounds(kIndexAppsContainer, container_frame); 132 view_model_->set_ideal_bounds(kIndexSearchResults, results_frame); 133} 134 135void ContentsView::AnimateToIdealBounds() { 136 CalculateIdealBounds(); 137 for (int i = 0; i < view_model_->view_size(); ++i) { 138 bounds_animator_->AnimateViewTo(view_model_->view_at(i), 139 view_model_->ideal_bounds(i)); 140 } 141} 142 143void ContentsView::ShowSearchResults(bool show) { 144 SetShowState(show ? SHOW_SEARCH_RESULTS : SHOW_APPS); 145} 146 147void ContentsView::ShowFolderContent(AppListFolderItem* item) { 148 apps_container_view_->ShowActiveFolder(item); 149} 150 151void ContentsView::Prerender() { 152 const int selected_page = std::max(0, pagination_model_->selected_page()); 153 apps_container_view_->apps_grid_view()->Prerender(selected_page); 154} 155 156gfx::Size ContentsView::GetPreferredSize() { 157 const gfx::Size container_size = GetAppsContainerView(view_model_.get())-> 158 apps_grid_view()->GetPreferredSize(); 159 const gfx::Size results_size = 160 GetSearchResultListView(view_model_.get())->GetPreferredSize(); 161 162 int width = std::max(container_size.width(), results_size.width()); 163 int height = std::max(container_size.height(), results_size.height()); 164 return gfx::Size(width, height); 165} 166 167void ContentsView::Layout() { 168 CalculateIdealBounds(); 169 views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_); 170} 171 172bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) { 173 switch (show_state_) { 174 case SHOW_APPS: 175 return GetAppsContainerView(view_model_.get())->OnKeyPressed(event); 176 case SHOW_SEARCH_RESULTS: 177 return GetSearchResultListView(view_model_.get())->OnKeyPressed(event); 178 default: 179 NOTREACHED() << "Unknown show state " << show_state_; 180 } 181 return false; 182} 183 184bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) { 185 if (show_state_ != SHOW_APPS) 186 return false; 187 188 int offset; 189 if (abs(event.x_offset()) > abs(event.y_offset())) 190 offset = event.x_offset(); 191 else 192 offset = event.y_offset(); 193 194 if (abs(offset) > kMinMouseWheelToSwitchPage) { 195 if (!pagination_model_->has_transition()) { 196 pagination_model_->SelectPageRelative( 197 offset > 0 ? -1 : 1, true); 198 } 199 return true; 200 } 201 202 return false; 203} 204 205void ContentsView::OnGestureEvent(ui::GestureEvent* event) { 206 if (show_state_ != SHOW_APPS) 207 return; 208 209 switch (event->type()) { 210 case ui::ET_GESTURE_SCROLL_BEGIN: 211 pagination_model_->StartScroll(); 212 event->SetHandled(); 213 return; 214 case ui::ET_GESTURE_SCROLL_UPDATE: 215 // event->details.scroll_x() > 0 means moving contents to right. That is, 216 // transitioning to previous page. 217 pagination_model_->UpdateScroll( 218 event->details().scroll_x() / GetContentsBounds().width()); 219 event->SetHandled(); 220 return; 221 case ui::ET_GESTURE_SCROLL_END: 222 pagination_model_->EndScroll(pagination_model_-> 223 transition().progress < kFinishTransitionThreshold); 224 event->SetHandled(); 225 return; 226 case ui::ET_SCROLL_FLING_START: { 227 pagination_model_->EndScroll(true); 228 if (fabs(event->details().velocity_x()) > kMinHorizVelocityToSwitchPage) { 229 pagination_model_->SelectPageRelative( 230 event->details().velocity_x() < 0 ? 1 : -1, 231 true); 232 } 233 event->SetHandled(); 234 return; 235 } 236 default: 237 break; 238 } 239} 240 241void ContentsView::OnScrollEvent(ui::ScrollEvent* event) { 242 if (show_state_ != SHOW_APPS || 243 event->type() == ui::ET_SCROLL_FLING_CANCEL) { 244 return; 245 } 246 247 float offset; 248 if (abs(event->x_offset()) > abs(event->y_offset())) 249 offset = event->x_offset(); 250 else 251 offset = event->y_offset(); 252 253 if (abs(offset) > kMinScrollToSwitchPage) { 254 if (!pagination_model_->has_transition()) { 255 pagination_model_->SelectPageRelative(offset > 0 ? -1 : 1, 256 true); 257 } 258 event->SetHandled(); 259 event->StopPropagation(); 260 } 261} 262 263} // namespace app_list 264