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"
10 #include "base/logging.h"
11 #include "grit/ui_resources.h"
12 #include "ui/app_list/app_list_constants.h"
13 #include "ui/app_list/app_list_switches.h"
14 #include "ui/app_list/app_list_view_delegate.h"
15 #include "ui/app_list/views/app_list_folder_view.h"
16 #include "ui/app_list/views/app_list_main_view.h"
17 #include "ui/app_list/views/apps_container_view.h"
18 #include "ui/app_list/views/apps_grid_view.h"
19 #include "ui/app_list/views/contents_switcher_view.h"
20 #include "ui/app_list/views/search_result_list_view.h"
21 #include "ui/app_list/views/start_page_view.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 page_before_search_(0) {
45 pagination_model_.AddObserver(this);
48 ContentsView::~ContentsView() {
49 pagination_model_.RemoveObserver(this);
50 if (contents_switcher_view_)
51 pagination_model_.RemoveObserver(contents_switcher_view_);
54 void ContentsView::InitNamedPages(AppListModel* model,
55 AppListViewDelegate* view_delegate) {
58 if (app_list::switches::IsExperimentalAppListEnabled()) {
59 std::vector<views::View*> custom_page_views =
60 view_delegate->CreateCustomPageWebViews(GetLocalBounds().size());
61 for (std::vector<views::View*>::const_iterator it =
62 custom_page_views.begin();
63 it != custom_page_views.end();
65 AddLauncherPage(*it, IDR_APP_LIST_NOTIFICATIONS_ICON);
68 start_page_view_ = new StartPageView(app_list_main_view_, view_delegate);
70 start_page_view_, IDR_APP_LIST_SEARCH_ICON, NAMED_PAGE_START);
72 search_results_view_ =
73 new SearchResultListView(app_list_main_view_, view_delegate);
74 AddLauncherPage(search_results_view_, 0, NAMED_PAGE_SEARCH_RESULTS);
75 search_results_view_->SetResults(model->results());
78 apps_container_view_ = new AppsContainerView(app_list_main_view_, model);
80 int initial_page_index = AddLauncherPage(
81 apps_container_view_, IDR_APP_LIST_APPS_ICON, NAMED_PAGE_APPS);
82 if (app_list::switches::IsExperimentalAppListEnabled())
83 initial_page_index = GetPageIndexForNamedPage(NAMED_PAGE_START);
85 page_before_search_ = initial_page_index;
86 pagination_model_.SelectPage(initial_page_index, false);
88 // Needed to update the main search box visibility.
89 ActivePageChanged(false);
92 void ContentsView::CancelDrag() {
93 if (apps_container_view_->apps_grid_view()->has_dragged_view())
94 apps_container_view_->apps_grid_view()->EndDrag(true);
95 if (apps_container_view_->app_list_folder_view()
97 ->has_dragged_view()) {
98 apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag(
103 void ContentsView::SetDragAndDropHostOfCurrentAppList(
104 ApplicationDragAndDropHost* drag_and_drop_host) {
105 apps_container_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
108 void ContentsView::SetContentsSwitcherView(
109 ContentsSwitcherView* contents_switcher_view) {
110 DCHECK(!contents_switcher_view_);
111 contents_switcher_view_ = contents_switcher_view;
112 if (contents_switcher_view_)
113 pagination_model_.AddObserver(contents_switcher_view_);
116 void ContentsView::SetActivePage(int page_index) {
117 if (GetActivePageIndex() == page_index)
120 SetActivePageInternal(page_index, false);
123 int ContentsView::GetActivePageIndex() const {
124 // The active page is changed at the beginning of an animation, not the end.
125 return pagination_model_.SelectedTargetPage();
128 bool ContentsView::IsNamedPageActive(NamedPage named_page) const {
129 std::map<NamedPage, int>::const_iterator it =
130 named_page_to_view_.find(named_page);
131 if (it == named_page_to_view_.end())
133 return it->second == GetActivePageIndex();
136 int ContentsView::GetPageIndexForNamedPage(NamedPage named_page) const {
137 // Find the index of the view corresponding to the given named_page.
138 std::map<NamedPage, int>::const_iterator it =
139 named_page_to_view_.find(named_page);
140 // GetPageIndexForNamedPage should never be called on a named_page that does
141 // not have a corresponding view.
142 DCHECK(it != named_page_to_view_.end());
146 int ContentsView::NumLauncherPages() const {
147 return pagination_model_.total_pages();
150 void ContentsView::SetActivePageInternal(int page_index,
151 bool show_search_results) {
152 if (!show_search_results)
153 page_before_search_ = page_index;
154 // Start animating to the new page.
155 pagination_model_.SelectPage(page_index, true);
156 ActivePageChanged(show_search_results);
159 void ContentsView::ActivePageChanged(bool show_search_results) {
160 // TODO(xiyuan): Highlight default match instead of the first.
161 if (IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS) &&
162 search_results_view_->visible()) {
163 search_results_view_->SetSelectedIndex(0);
165 if (search_results_view_)
166 search_results_view_->UpdateAutoLaunchState();
168 if (IsNamedPageActive(NAMED_PAGE_START)) {
169 if (show_search_results)
170 start_page_view_->ShowSearchResults();
172 start_page_view_->Reset();
175 // Notify parent AppListMainView of the page change.
176 app_list_main_view_->UpdateSearchBoxVisibility();
179 void ContentsView::ShowSearchResults(bool show) {
180 int search_page = GetPageIndexForNamedPage(
181 app_list::switches::IsExperimentalAppListEnabled()
183 : NAMED_PAGE_SEARCH_RESULTS);
185 SetActivePageInternal(show ? search_page : page_before_search_, show);
188 bool ContentsView::IsShowingSearchResults() const {
189 return app_list::switches::IsExperimentalAppListEnabled()
190 ? IsNamedPageActive(NAMED_PAGE_START) &&
191 start_page_view_->IsShowingSearchResults()
192 : IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS);
195 void ContentsView::UpdatePageBounds() {
196 gfx::Rect rect(GetContentsBounds());
200 // The bounds calculations will potentially be mid-transition (depending on
201 // the state of the PaginationModel).
202 int current_page = std::max(0, pagination_model_.selected_page());
203 int target_page = current_page;
205 if (pagination_model_.has_transition()) {
206 const PaginationModel::Transition& transition =
207 pagination_model_.transition();
208 if (pagination_model_.is_valid_page(transition.target_page)) {
209 target_page = transition.target_page;
210 progress = transition.progress;
214 gfx::Rect incoming_target(rect);
215 gfx::Rect outgoing_target(rect);
216 int dir = target_page > current_page ? -1 : 1;
218 if (app_list::switches::IsExperimentalAppListEnabled()) {
219 // The experimental app list transitions horizontally.
220 int page_width = rect.width();
221 int transition_offset = progress * page_width * dir;
223 outgoing_target.set_x(transition_offset);
224 incoming_target.set_x(dir < 0 ? transition_offset + page_width
225 : transition_offset - page_width);
227 // The normal app list transitions vertically.
228 int page_height = rect.height();
229 int transition_offset = progress * page_height * dir;
231 outgoing_target.set_y(transition_offset);
232 incoming_target.set_y(dir < 0 ? transition_offset + page_height
233 : transition_offset - page_height);
236 view_model_->view_at(current_page)->SetBoundsRect(outgoing_target);
237 view_model_->view_at(target_page)->SetBoundsRect(incoming_target);
240 PaginationModel* ContentsView::GetAppsPaginationModel() {
241 return apps_container_view_->apps_grid_view()->pagination_model();
244 void ContentsView::ShowFolderContent(AppListFolderItem* item) {
245 apps_container_view_->ShowActiveFolder(item);
248 void ContentsView::Prerender() {
249 const int selected_page =
250 std::max(0, GetAppsPaginationModel()->selected_page());
251 apps_container_view_->apps_grid_view()->Prerender(selected_page);
254 views::View* ContentsView::GetPageView(int index) {
255 return view_model_->view_at(index);
258 void ContentsView::AddBlankPageForTesting() {
259 AddLauncherPage(new views::View, 0);
262 int ContentsView::AddLauncherPage(views::View* view, int resource_id) {
263 int page_index = view_model_->view_size();
265 view_model_->Add(view, page_index);
266 if (contents_switcher_view_)
267 contents_switcher_view_->AddSwitcherButton(resource_id, page_index);
268 pagination_model_.SetTotalPages(view_model_->view_size());
272 int ContentsView::AddLauncherPage(views::View* view,
274 NamedPage named_page) {
275 int page_index = AddLauncherPage(view, resource_id);
276 named_page_to_view_.insert(std::pair<NamedPage, int>(named_page, page_index));
280 gfx::Size ContentsView::GetPreferredSize() const {
281 const gfx::Size container_size =
282 apps_container_view_->apps_grid_view()->GetPreferredSize();
283 const gfx::Size results_size = search_results_view_
284 ? search_results_view_->GetPreferredSize()
287 int width = std::max(container_size.width(), results_size.width());
288 int height = std::max(container_size.height(), results_size.height());
289 return gfx::Size(width, height);
292 void ContentsView::Layout() {
293 // Immediately finish all current animations.
294 pagination_model_.FinishAnimation();
296 // Move the current view onto the screen, and all other views off screen to
297 // the left. (Since we are not animating, we don't need to be careful about
298 // which side we place the off-screen views onto.)
299 gfx::Rect rect(GetContentsBounds());
303 gfx::Rect offscreen_target(rect);
304 offscreen_target.set_x(-rect.width());
306 for (int i = 0; i < view_model_->view_size(); ++i) {
307 view_model_->view_at(i)->SetBoundsRect(
308 i == pagination_model_.SelectedTargetPage() ? rect : offscreen_target);
312 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
313 return view_model_->view_at(GetActivePageIndex())->OnKeyPressed(event);
316 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
317 if (!IsNamedPageActive(NAMED_PAGE_APPS))
321 if (abs(event.x_offset()) > abs(event.y_offset()))
322 offset = event.x_offset();
324 offset = event.y_offset();
326 if (abs(offset) > kMinMouseWheelToSwitchPage) {
327 if (!GetAppsPaginationModel()->has_transition()) {
328 GetAppsPaginationModel()->SelectPageRelative(offset > 0 ? -1 : 1, true);
336 void ContentsView::TotalPagesChanged() {
339 void ContentsView::SelectedPageChanged(int old_selected, int new_selected) {
342 void ContentsView::TransitionStarted() {
345 void ContentsView::TransitionChanged() {
349 void ContentsView::OnGestureEvent(ui::GestureEvent* event) {
350 if (!IsNamedPageActive(NAMED_PAGE_APPS))
353 switch (event->type()) {
354 case ui::ET_GESTURE_SCROLL_BEGIN:
355 GetAppsPaginationModel()->StartScroll();
358 case ui::ET_GESTURE_SCROLL_UPDATE:
359 // event->details.scroll_x() > 0 means moving contents to right. That is,
360 // transitioning to previous page.
361 GetAppsPaginationModel()->UpdateScroll(event->details().scroll_x() /
362 GetContentsBounds().width());
365 case ui::ET_GESTURE_SCROLL_END:
366 GetAppsPaginationModel()->EndScroll(
367 GetAppsPaginationModel()->transition().progress <
368 kFinishTransitionThreshold);
371 case ui::ET_SCROLL_FLING_START: {
372 GetAppsPaginationModel()->EndScroll(true);
373 if (fabs(event->details().velocity_x()) > kMinHorizVelocityToSwitchPage) {
374 GetAppsPaginationModel()->SelectPageRelative(
375 event->details().velocity_x() < 0 ? 1 : -1, true);
385 void ContentsView::OnScrollEvent(ui::ScrollEvent* event) {
386 if (!IsNamedPageActive(NAMED_PAGE_APPS) ||
387 event->type() == ui::ET_SCROLL_FLING_CANCEL) {
392 if (std::abs(event->x_offset()) > std::abs(event->y_offset()))
393 offset = event->x_offset();
395 offset = event->y_offset();
397 if (std::abs(offset) > kMinScrollToSwitchPage) {
398 if (!GetAppsPaginationModel()->has_transition()) {
399 GetAppsPaginationModel()->SelectPageRelative(offset > 0 ? -1 : 1, true);
402 event->StopPropagation();
406 } // namespace app_list