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"
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"
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
));
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
,
66 apps_grid_view
->SetLayout(kPreferredIconDimension
,
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(
78 AddChildView(search_results_view
);
79 view_model_
->Add(search_results_view
, kIndexSearchResults
);
82 ContentsView::~ContentsView() {
85 void ContentsView::SetModel(AppListModel
* model
) {
87 GetAppsGridView(view_model_
.get())->SetModel(model
->apps());
88 GetSearchResultListView(view_model_
.get())->SetResults(model
->results());
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
)
99 show_state_
= show_state
;
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());
120 const int x
= rect
.x();
121 const int width
= rect
.width();
123 // AppsGridView and PageSwitcher uses a vertical box layout.
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_
) {
145 results_frame
.Offset(0, -contents_area_height
);
147 case SHOW_SEARCH_RESULTS
:
148 grid_frame
.Offset(0, contents_area_height
);
149 page_switcher_frame
.Offset(0, contents_area_height
);
152 NOTREACHED() << "Unknown show_state_ " << show_state_
;
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,
220 return ui::ER_CONSUMED
;
226 return ui::ER_UNHANDLED
;
229 bool ContentsView::OnKeyPressed(const ui::KeyEvent
& event
) {
230 switch (show_state_
) {
232 return GetAppsGridView(view_model_
.get())->OnKeyPressed(event
);
233 case SHOW_SEARCH_RESULTS
:
234 return GetSearchResultListView(view_model_
.get())->OnKeyPressed(event
);
236 NOTREACHED() << "Unknown show state " << show_state_
;
241 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent
& event
) {
242 if (show_state_
!= SHOW_APPS
)
245 if (abs(event
.offset()) > kMinMouseWheelToSwitchPage
) {
246 if (!pagination_model_
->has_transition())
247 pagination_model_
->SelectPageRelative(event
.offset() > 0 ? -1 : 1, true);
254 bool ContentsView::OnScrollEvent(const ui::ScrollEvent
& event
) {
255 if (show_state_
!= SHOW_APPS
)
258 if (abs(event
.x_offset()) > kMinScrollToSwitchPage
) {
259 if (!pagination_model_
->has_transition()) {
260 pagination_model_
->SelectPageRelative(event
.x_offset() > 0 ? -1 : 1,
269 } // namespace app_list