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