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/views/contents_view.h"
9 #include "base/logging.h"
10 #include "grit/ui_resources.h"
11 #include "ui/app_list/app_list_constants.h"
12 #include "ui/app_list/app_list_switches.h"
13 #include "ui/app_list/app_list_view_delegate.h"
14 #include "ui/app_list/views/app_list_folder_view.h"
15 #include "ui/app_list/views/app_list_main_view.h"
16 #include "ui/app_list/views/apps_container_view.h"
17 #include "ui/app_list/views/apps_grid_view.h"
18 #include "ui/app_list/views/contents_switcher_view.h"
19 #include "ui/app_list/views/search_result_list_view.h"
20 #include "ui/app_list/views/start_page_view.h"
21 #include "ui/base/resource/resource_bundle.h"
22 #include "ui/events/event.h"
23 #include "ui/views/view_model.h"
24 #include "ui/views/view_model_utils.h"
30 const int kMinMouseWheelToSwitchPage
= 20;
31 const int kMinScrollToSwitchPage
= 20;
32 const int kMinHorizVelocityToSwitchPage
= 800;
34 const double kFinishTransitionThreshold
= 0.33;
38 ContentsView::ContentsView(AppListMainView
* app_list_main_view
)
39 : search_results_view_(NULL
),
40 start_page_view_(NULL
),
41 app_list_main_view_(app_list_main_view
),
42 contents_switcher_view_(NULL
),
43 view_model_(new views::ViewModel
) {
44 pagination_model_
.AddObserver(this);
47 ContentsView::~ContentsView() {
48 pagination_model_
.RemoveObserver(this);
51 void ContentsView::InitNamedPages(AppListModel
* model
,
52 AppListViewDelegate
* view_delegate
) {
55 if (app_list::switches::IsExperimentalAppListEnabled()) {
56 start_page_view_
= new StartPageView(app_list_main_view_
, view_delegate
);
58 start_page_view_
, IDR_APP_LIST_SEARCH_ICON
, NAMED_PAGE_START
);
60 search_results_view_
=
61 new SearchResultListView(app_list_main_view_
, view_delegate
);
62 AddLauncherPage(search_results_view_
, 0, NAMED_PAGE_SEARCH_RESULTS
);
63 search_results_view_
->SetResults(model
->results());
66 apps_container_view_
= new AppsContainerView(app_list_main_view_
, model
);
67 int apps_page_index
= AddLauncherPage(
68 apps_container_view_
, IDR_APP_LIST_APPS_ICON
, NAMED_PAGE_APPS
);
70 pagination_model_
.SelectPage(apps_page_index
, false);
73 void ContentsView::CancelDrag() {
74 if (apps_container_view_
->apps_grid_view()->has_dragged_view())
75 apps_container_view_
->apps_grid_view()->EndDrag(true);
76 if (apps_container_view_
->app_list_folder_view()
78 ->has_dragged_view()) {
79 apps_container_view_
->app_list_folder_view()->items_grid_view()->EndDrag(
84 void ContentsView::SetDragAndDropHostOfCurrentAppList(
85 ApplicationDragAndDropHost
* drag_and_drop_host
) {
86 apps_container_view_
->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host
);
89 void ContentsView::SetActivePage(int page_index
) {
90 if (GetActivePageIndex() == page_index
)
93 SetActivePageInternal(page_index
, false);
96 int ContentsView::GetActivePageIndex() const {
97 // The active page is changed at the beginning of an animation, not the end.
98 return pagination_model_
.SelectedTargetPage();
101 bool ContentsView::IsNamedPageActive(NamedPage named_page
) const {
102 std::map
<NamedPage
, int>::const_iterator it
=
103 named_page_to_view_
.find(named_page
);
104 if (it
== named_page_to_view_
.end())
106 return it
->second
== GetActivePageIndex();
109 int ContentsView::GetPageIndexForNamedPage(NamedPage named_page
) const {
110 // Find the index of the view corresponding to the given named_page.
111 std::map
<NamedPage
, int>::const_iterator it
=
112 named_page_to_view_
.find(named_page
);
113 // GetPageIndexForNamedPage should never be called on a named_page that does
114 // not have a corresponding view.
115 DCHECK(it
!= named_page_to_view_
.end());
119 int ContentsView::NumLauncherPages() const {
120 return pagination_model_
.total_pages();
123 void ContentsView::SetActivePageInternal(int page_index
,
124 bool show_search_results
) {
125 // Start animating to the new page.
126 pagination_model_
.SelectPage(page_index
, true);
127 ActivePageChanged(show_search_results
);
130 void ContentsView::ActivePageChanged(bool show_search_results
) {
131 // TODO(xiyuan): Highlight default match instead of the first.
132 if (IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS
) &&
133 search_results_view_
->visible()) {
134 search_results_view_
->SetSelectedIndex(0);
136 if (search_results_view_
)
137 search_results_view_
->UpdateAutoLaunchState();
139 if (IsNamedPageActive(NAMED_PAGE_START
)) {
140 if (show_search_results
)
141 start_page_view_
->ShowSearchResults();
143 start_page_view_
->Reset();
146 // Notify parent AppListMainView of the page change.
147 app_list_main_view_
->UpdateSearchBoxVisibility();
150 void ContentsView::ShowSearchResults(bool show
) {
151 NamedPage new_named_page
= show
? NAMED_PAGE_SEARCH_RESULTS
: NAMED_PAGE_APPS
;
152 if (app_list::switches::IsExperimentalAppListEnabled())
153 new_named_page
= NAMED_PAGE_START
;
155 SetActivePageInternal(GetPageIndexForNamedPage(new_named_page
), show
);
158 bool ContentsView::IsShowingSearchResults() const {
159 return app_list::switches::IsExperimentalAppListEnabled()
160 ? IsNamedPageActive(NAMED_PAGE_START
) &&
161 start_page_view_
->IsShowingSearchResults()
162 : IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS
);
165 void ContentsView::UpdatePageBounds() {
166 gfx::Rect
rect(GetContentsBounds());
170 // The bounds calculations will potentially be mid-transition (depending on
171 // the state of the PaginationModel).
172 int current_page
= std::max(0, pagination_model_
.selected_page());
173 int target_page
= current_page
;
175 if (pagination_model_
.has_transition()) {
176 const PaginationModel::Transition
& transition
=
177 pagination_model_
.transition();
178 if (pagination_model_
.is_valid_page(transition
.target_page
)) {
179 target_page
= transition
.target_page
;
180 progress
= transition
.progress
;
184 gfx::Rect
incoming_target(rect
);
185 gfx::Rect
outgoing_target(rect
);
186 int dir
= target_page
> current_page
? -1 : 1;
188 if (app_list::switches::IsExperimentalAppListEnabled()) {
189 // The experimental app list transitions horizontally.
190 int page_width
= rect
.width();
191 int transition_offset
= progress
* page_width
* dir
;
193 outgoing_target
.set_x(transition_offset
);
194 incoming_target
.set_x(dir
< 0 ? transition_offset
+ page_width
195 : transition_offset
- page_width
);
197 // The normal app list transitions vertically.
198 int page_height
= rect
.height();
199 int transition_offset
= progress
* page_height
* dir
;
201 outgoing_target
.set_y(transition_offset
);
202 incoming_target
.set_y(dir
< 0 ? transition_offset
+ page_height
203 : transition_offset
- page_height
);
206 view_model_
->view_at(current_page
)->SetBoundsRect(outgoing_target
);
207 view_model_
->view_at(target_page
)->SetBoundsRect(incoming_target
);
210 PaginationModel
* ContentsView::GetAppsPaginationModel() {
211 return apps_container_view_
->apps_grid_view()->pagination_model();
214 void ContentsView::ShowFolderContent(AppListFolderItem
* item
) {
215 apps_container_view_
->ShowActiveFolder(item
);
218 void ContentsView::Prerender() {
219 const int selected_page
=
220 std::max(0, GetAppsPaginationModel()->selected_page());
221 apps_container_view_
->apps_grid_view()->Prerender(selected_page
);
224 views::View
* ContentsView::GetPageView(int index
) {
225 return view_model_
->view_at(index
);
228 void ContentsView::AddBlankPageForTesting() {
229 AddLauncherPage(new views::View
, 0);
232 int ContentsView::AddLauncherPage(views::View
* view
, int resource_id
) {
233 int page_index
= view_model_
->view_size();
235 view_model_
->Add(view
, page_index
);
236 pagination_model_
.SetTotalPages(view_model_
->view_size());
237 if (contents_switcher_view_
)
238 contents_switcher_view_
->AddSwitcherButton(resource_id
, page_index
);
242 int ContentsView::AddLauncherPage(views::View
* view
,
244 NamedPage named_page
) {
245 int page_index
= AddLauncherPage(view
, resource_id
);
246 named_page_to_view_
.insert(std::pair
<NamedPage
, int>(named_page
, page_index
));
250 gfx::Size
ContentsView::GetPreferredSize() const {
251 const gfx::Size container_size
=
252 apps_container_view_
->apps_grid_view()->GetPreferredSize();
253 const gfx::Size results_size
= search_results_view_
254 ? search_results_view_
->GetPreferredSize()
257 int width
= std::max(container_size
.width(), results_size
.width());
258 int height
= std::max(container_size
.height(), results_size
.height());
259 return gfx::Size(width
, height
);
262 void ContentsView::Layout() {
263 // Immediately finish all current animations.
264 pagination_model_
.FinishAnimation();
266 // Move the current view onto the screen, and all other views off screen to
267 // the left. (Since we are not animating, we don't need to be careful about
268 // which side we place the off-screen views onto.)
269 gfx::Rect
rect(GetContentsBounds());
273 gfx::Rect
offscreen_target(rect
);
274 offscreen_target
.set_x(-rect
.width());
276 for (int i
= 0; i
< view_model_
->view_size(); ++i
) {
277 view_model_
->view_at(i
)->SetBoundsRect(
278 i
== pagination_model_
.SelectedTargetPage() ? rect
: offscreen_target
);
282 bool ContentsView::OnKeyPressed(const ui::KeyEvent
& event
) {
283 return view_model_
->view_at(GetActivePageIndex())->OnKeyPressed(event
);
286 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent
& event
) {
287 if (!IsNamedPageActive(NAMED_PAGE_APPS
))
291 if (abs(event
.x_offset()) > abs(event
.y_offset()))
292 offset
= event
.x_offset();
294 offset
= event
.y_offset();
296 if (abs(offset
) > kMinMouseWheelToSwitchPage
) {
297 if (!GetAppsPaginationModel()->has_transition()) {
298 GetAppsPaginationModel()->SelectPageRelative(offset
> 0 ? -1 : 1, true);
306 void ContentsView::TotalPagesChanged() {
309 void ContentsView::SelectedPageChanged(int old_selected
, int new_selected
) {
312 void ContentsView::TransitionStarted() {
315 void ContentsView::TransitionChanged() {
319 void ContentsView::OnGestureEvent(ui::GestureEvent
* event
) {
320 if (!IsNamedPageActive(NAMED_PAGE_APPS
))
323 switch (event
->type()) {
324 case ui::ET_GESTURE_SCROLL_BEGIN
:
325 GetAppsPaginationModel()->StartScroll();
328 case ui::ET_GESTURE_SCROLL_UPDATE
:
329 // event->details.scroll_x() > 0 means moving contents to right. That is,
330 // transitioning to previous page.
331 GetAppsPaginationModel()->UpdateScroll(event
->details().scroll_x() /
332 GetContentsBounds().width());
335 case ui::ET_GESTURE_SCROLL_END
:
336 GetAppsPaginationModel()->EndScroll(
337 GetAppsPaginationModel()->transition().progress
<
338 kFinishTransitionThreshold
);
341 case ui::ET_SCROLL_FLING_START
: {
342 GetAppsPaginationModel()->EndScroll(true);
343 if (fabs(event
->details().velocity_x()) > kMinHorizVelocityToSwitchPage
) {
344 GetAppsPaginationModel()->SelectPageRelative(
345 event
->details().velocity_x() < 0 ? 1 : -1, true);
355 void ContentsView::OnScrollEvent(ui::ScrollEvent
* event
) {
356 if (!IsNamedPageActive(NAMED_PAGE_APPS
) ||
357 event
->type() == ui::ET_SCROLL_FLING_CANCEL
) {
362 if (std::abs(event
->x_offset()) > std::abs(event
->y_offset()))
363 offset
= event
->x_offset();
365 offset
= event
->y_offset();
367 if (std::abs(offset
) > kMinScrollToSwitchPage
) {
368 if (!GetAppsPaginationModel()->has_transition()) {
369 GetAppsPaginationModel()->SelectPageRelative(offset
> 0 ? -1 : 1, true);
372 event
->StopPropagation();
376 } // namespace app_list