WebKit roll 130275:130304
[chromium-blink-merge.git] / ui / app_list / contents_view.cc
blobac3ee40bd43407393aaaea560fa4f746d5d033e9
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/contents_view.h"
7 #include <algorithm>
9 #include "ui/app_list/app_list_view.h"
10 #include "ui/app_list/apps_grid_view.h"
11 #include "ui/app_list/page_switcher.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"
19 namespace app_list {
21 namespace {
23 const int kPreferredIconDimension = 48;
24 const int kPreferredCols = 4;
25 const int kPreferredRows = 4;
27 // Indexes of interesting views in ViewModel of ContentsView.
28 const int kIndexAppsGrid = 0;
29 const int kIndexPageSwitcher = 1;
30 const int kIndexSearchResults = 2;
32 const int kMinMouseWheelToSwitchPage = 20;
33 const int kMinScrollToSwitchPage = 20;
34 const int kMinHorizVelocityToSwitchPage = 800;
36 const double kFinishTransitionThreshold = 0.33;
37 const int kTransitionAnimationDurationInMs = 180;
39 // Helpers to get certain child view from |model|.
40 AppsGridView* GetAppsGridView(views::ViewModel* model) {
41 return static_cast<AppsGridView*>(model->view_at(kIndexAppsGrid));
44 PageSwitcher* GetPageSwitcherView(views::ViewModel* model) {
45 return static_cast<PageSwitcher*>(model->view_at(kIndexPageSwitcher));
48 SearchResultListView* GetSearchResultListView(views::ViewModel* model) {
49 return static_cast<SearchResultListView*>(
50 model->view_at(kIndexSearchResults));
53 } // namespace
55 ContentsView::ContentsView(AppListView* app_list_view,
56 PaginationModel* pagination_model)
57 : show_state_(SHOW_APPS),
58 pagination_model_(pagination_model),
59 view_model_(new views::ViewModel),
60 ALLOW_THIS_IN_INITIALIZER_LIST(
61 bounds_animator_(new views::BoundsAnimator(this))) {
62 pagination_model_->SetTransitionDuration(kTransitionAnimationDurationInMs);
64 AppsGridView* apps_grid_view = new AppsGridView(app_list_view,
65 pagination_model);
66 apps_grid_view->SetLayout(kPreferredIconDimension,
67 kPreferredCols,
68 kPreferredRows);
69 AddChildView(apps_grid_view);
70 view_model_->Add(apps_grid_view, kIndexAppsGrid);
72 PageSwitcher* page_switcher_view = new PageSwitcher(pagination_model);
73 AddChildView(page_switcher_view);
74 view_model_->Add(page_switcher_view, kIndexPageSwitcher);
76 SearchResultListView* search_results_view = new SearchResultListView(
77 app_list_view);
78 AddChildView(search_results_view);
79 view_model_->Add(search_results_view, kIndexSearchResults);
82 ContentsView::~ContentsView() {
85 void ContentsView::SetModel(AppListModel* model) {
86 if (model) {
87 GetAppsGridView(view_model_.get())->SetModel(model->apps());
88 GetSearchResultListView(view_model_.get())->SetResults(model->results());
89 } else {
90 GetAppsGridView(view_model_.get())->SetModel(NULL);
91 GetSearchResultListView(view_model_.get())->SetResults(NULL);
95 void ContentsView::SetShowState(ShowState show_state) {
96 if (show_state_ == show_state)
97 return;
99 show_state_ = show_state;
100 ShowStateChanged();
103 void ContentsView::ShowStateChanged() {
104 if (show_state_ == SHOW_SEARCH_RESULTS) {
105 // TODO(xiyuan): Highlight default match instead of the first.
106 SearchResultListView* results_view =
107 GetSearchResultListView(view_model_.get());
108 if (results_view->visible())
109 results_view->SetSelectedIndex(0);
112 AnimateToIdealBounds();
115 void ContentsView::CalculateIdealBounds() {
116 gfx::Rect rect(GetContentsBounds());
117 if (rect.IsEmpty())
118 return;
120 const int x = rect.x();
121 const int width = rect.width();
123 // AppsGridView and PageSwitcher uses a vertical box layout.
124 int y = rect.y();
125 const int grid_height =
126 GetAppsGridView(view_model_.get())->GetPreferredSize().height();
127 gfx::Rect grid_frame(gfx::Point(x, y), gfx::Size(width, grid_height));
128 grid_frame = rect.Intersect(grid_frame);
130 y = grid_frame.bottom();
131 const int page_switcher_height = rect.bottom() - y;
132 gfx::Rect page_switcher_frame(gfx::Point(x, y),
133 gfx::Size(width, page_switcher_height));
134 page_switcher_frame = rect.Intersect(page_switcher_frame);
136 // SearchResultListView occupies the whole space when visible.
137 gfx::Rect results_frame(rect);
139 // Offsets apps grid, page switcher and result list based on |show_state_|.
140 // SearchResultListView is on top of apps grid + page switcher. Visible view
141 // is left in visible area and invisible ones is put out of the visible area.
142 int contents_area_height = rect.height();
143 switch (show_state_) {
144 case SHOW_APPS:
145 results_frame.Offset(0, -contents_area_height);
146 break;
147 case SHOW_SEARCH_RESULTS:
148 grid_frame.Offset(0, contents_area_height);
149 page_switcher_frame.Offset(0, contents_area_height);
150 break;
151 default:
152 NOTREACHED() << "Unknown show_state_ " << show_state_;
153 break;
156 view_model_->set_ideal_bounds(kIndexAppsGrid, grid_frame);
157 view_model_->set_ideal_bounds(kIndexPageSwitcher, page_switcher_frame);
158 view_model_->set_ideal_bounds(kIndexSearchResults, results_frame);
161 void ContentsView::AnimateToIdealBounds() {
162 CalculateIdealBounds();
163 for (int i = 0; i < view_model_->view_size(); ++i) {
164 bounds_animator_->AnimateViewTo(view_model_->view_at(i),
165 view_model_->ideal_bounds(i));
169 void ContentsView::ShowSearchResults(bool show) {
170 SetShowState(show ? SHOW_SEARCH_RESULTS : SHOW_APPS);
173 gfx::Size ContentsView::GetPreferredSize() {
174 const gfx::Size grid_size =
175 GetAppsGridView(view_model_.get())->GetPreferredSize();
176 const gfx::Size page_switcher_size =
177 GetPageSwitcherView(view_model_.get())->GetPreferredSize();
178 const gfx::Size results_size =
179 GetSearchResultListView(view_model_.get())->GetPreferredSize();
181 int width = std::max(
182 std::max(grid_size.width(), page_switcher_size.width()),
183 results_size.width());
184 int height = std::max(grid_size.height() + page_switcher_size.height(),
185 results_size.height());
186 return gfx::Size(width, height);
189 void ContentsView::Layout() {
190 CalculateIdealBounds();
191 views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
194 ui::EventResult ContentsView::OnGestureEvent(
195 const ui::GestureEvent& event) {
196 if (show_state_ != SHOW_APPS)
197 return ui::ER_UNHANDLED;
199 switch (event.type()) {
200 case ui::ET_GESTURE_SCROLL_BEGIN:
201 pagination_model_->StartScroll();
202 return ui::ER_CONSUMED;
203 case ui::ET_GESTURE_SCROLL_UPDATE:
204 // event.details.scroll_x() > 0 means moving contents to right. That is,
205 // transitioning to previous page.
206 pagination_model_->UpdateScroll(
207 event.details().scroll_x() / GetContentsBounds().width());
208 return ui::ER_CONSUMED;
209 case ui::ET_GESTURE_SCROLL_END:
210 pagination_model_->EndScroll(pagination_model_->
211 transition().progress < kFinishTransitionThreshold);
212 return ui::ER_CONSUMED;
213 case ui::ET_SCROLL_FLING_START: {
214 pagination_model_->EndScroll(true);
215 if (fabs(event.details().velocity_x()) > kMinHorizVelocityToSwitchPage) {
216 pagination_model_->SelectPageRelative(
217 event.details().velocity_x() < 0 ? 1 : -1,
218 true);
220 return ui::ER_CONSUMED;
222 default:
223 break;
226 return ui::ER_UNHANDLED;
229 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
230 switch (show_state_) {
231 case SHOW_APPS:
232 return GetAppsGridView(view_model_.get())->OnKeyPressed(event);
233 case SHOW_SEARCH_RESULTS:
234 return GetSearchResultListView(view_model_.get())->OnKeyPressed(event);
235 default:
236 NOTREACHED() << "Unknown show state " << show_state_;
238 return false;
241 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
242 if (show_state_ != SHOW_APPS)
243 return false;
245 if (abs(event.offset()) > kMinMouseWheelToSwitchPage) {
246 if (!pagination_model_->has_transition())
247 pagination_model_->SelectPageRelative(event.offset() > 0 ? -1 : 1, true);
248 return true;
251 return false;
254 bool ContentsView::OnScrollEvent(const ui::ScrollEvent& event) {
255 if (show_state_ != SHOW_APPS)
256 return false;
258 if (abs(event.x_offset()) > kMinScrollToSwitchPage) {
259 if (!pagination_model_->has_transition()) {
260 pagination_model_->SelectPageRelative(event.x_offset() > 0 ? -1 : 1,
261 true);
263 return true;
266 return false;
269 } // namespace app_list