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/views/contents_view.h" 6 7 #include <algorithm> 8 #include <vector> 9 10 #include "base/logging.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/events/event.h" 22 #include "ui/gfx/animation/tween.h" 23 #include "ui/resources/grit/ui_resources.h" 24 #include "ui/views/view_model.h" 25 #include "ui/views/view_model_utils.h" 26 27 namespace app_list { 28 29 ContentsView::ContentsView(AppListMainView* app_list_main_view) 30 : search_results_view_(NULL), 31 start_page_view_(NULL), 32 app_list_main_view_(app_list_main_view), 33 contents_switcher_view_(NULL), 34 view_model_(new views::ViewModel), 35 page_before_search_(0) { 36 pagination_model_.SetTransitionDurations(kPageTransitionDurationInMs, 37 kOverscrollPageTransitionDurationMs); 38 pagination_model_.AddObserver(this); 39 } 40 41 ContentsView::~ContentsView() { 42 pagination_model_.RemoveObserver(this); 43 if (contents_switcher_view_) 44 pagination_model_.RemoveObserver(contents_switcher_view_); 45 } 46 47 void ContentsView::InitNamedPages(AppListModel* model, 48 AppListViewDelegate* view_delegate) { 49 DCHECK(model); 50 51 if (app_list::switches::IsExperimentalAppListEnabled()) { 52 std::vector<views::View*> custom_page_views = 53 view_delegate->CreateCustomPageWebViews(GetLocalBounds().size()); 54 for (std::vector<views::View*>::const_iterator it = 55 custom_page_views.begin(); 56 it != custom_page_views.end(); 57 ++it) { 58 AddLauncherPage(*it, IDR_APP_LIST_NOTIFICATIONS_ICON); 59 } 60 61 start_page_view_ = new StartPageView(app_list_main_view_, view_delegate); 62 AddLauncherPage( 63 start_page_view_, IDR_APP_LIST_SEARCH_ICON, NAMED_PAGE_START); 64 } else { 65 search_results_view_ = 66 new SearchResultListView(app_list_main_view_, view_delegate); 67 AddLauncherPage(search_results_view_, 0, NAMED_PAGE_SEARCH_RESULTS); 68 search_results_view_->SetResults(model->results()); 69 } 70 71 apps_container_view_ = new AppsContainerView(app_list_main_view_, model); 72 73 AddLauncherPage( 74 apps_container_view_, IDR_APP_LIST_APPS_ICON, NAMED_PAGE_APPS); 75 76 int initial_page_index = app_list::switches::IsExperimentalAppListEnabled() 77 ? GetPageIndexForNamedPage(NAMED_PAGE_START) 78 : GetPageIndexForNamedPage(NAMED_PAGE_APPS); 79 DCHECK_GE(initial_page_index, 0); 80 81 page_before_search_ = initial_page_index; 82 pagination_model_.SelectPage(initial_page_index, false); 83 84 // Needed to update the main search box visibility. 85 ActivePageChanged(false); 86 } 87 88 void ContentsView::CancelDrag() { 89 if (apps_container_view_->apps_grid_view()->has_dragged_view()) 90 apps_container_view_->apps_grid_view()->EndDrag(true); 91 if (apps_container_view_->app_list_folder_view() 92 ->items_grid_view() 93 ->has_dragged_view()) { 94 apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag( 95 true); 96 } 97 } 98 99 void ContentsView::SetDragAndDropHostOfCurrentAppList( 100 ApplicationDragAndDropHost* drag_and_drop_host) { 101 apps_container_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host); 102 } 103 104 void ContentsView::SetContentsSwitcherView( 105 ContentsSwitcherView* contents_switcher_view) { 106 DCHECK(!contents_switcher_view_); 107 contents_switcher_view_ = contents_switcher_view; 108 if (contents_switcher_view_) 109 pagination_model_.AddObserver(contents_switcher_view_); 110 } 111 112 void ContentsView::SetActivePage(int page_index) { 113 if (GetActivePageIndex() == page_index) 114 return; 115 116 SetActivePageInternal(page_index, false); 117 } 118 119 int ContentsView::GetActivePageIndex() const { 120 // The active page is changed at the beginning of an animation, not the end. 121 return pagination_model_.SelectedTargetPage(); 122 } 123 124 bool ContentsView::IsNamedPageActive(NamedPage named_page) const { 125 int active_page_index = GetActivePageIndex(); 126 return active_page_index >= 0 && 127 GetPageIndexForNamedPage(named_page) == active_page_index; 128 } 129 130 int ContentsView::GetPageIndexForNamedPage(NamedPage named_page) const { 131 // Find the index of the view corresponding to the given named_page. 132 std::map<NamedPage, int>::const_iterator it = 133 named_page_to_view_.find(named_page); 134 if (it == named_page_to_view_.end()) 135 return -1; 136 137 return it->second; 138 } 139 140 int ContentsView::NumLauncherPages() const { 141 return pagination_model_.total_pages(); 142 } 143 144 void ContentsView::SetActivePageInternal(int page_index, 145 bool show_search_results) { 146 if (!show_search_results) 147 page_before_search_ = page_index; 148 // Start animating to the new page. 149 pagination_model_.SelectPage(page_index, true); 150 ActivePageChanged(show_search_results); 151 } 152 153 void ContentsView::ActivePageChanged(bool show_search_results) { 154 // TODO(xiyuan): Highlight default match instead of the first. 155 if (IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS) && 156 search_results_view_->visible()) { 157 search_results_view_->SetSelectedIndex(0); 158 } 159 if (search_results_view_) 160 search_results_view_->UpdateAutoLaunchState(); 161 162 if (IsNamedPageActive(NAMED_PAGE_START)) { 163 if (show_search_results) 164 start_page_view_->ShowSearchResults(); 165 else 166 start_page_view_->Reset(); 167 } 168 169 // Notify parent AppListMainView of the page change. 170 app_list_main_view_->UpdateSearchBoxVisibility(); 171 } 172 173 void ContentsView::ShowSearchResults(bool show) { 174 int search_page = GetPageIndexForNamedPage( 175 app_list::switches::IsExperimentalAppListEnabled() 176 ? NAMED_PAGE_START 177 : NAMED_PAGE_SEARCH_RESULTS); 178 DCHECK_GE(search_page, 0); 179 180 SetActivePageInternal(show ? search_page : page_before_search_, show); 181 } 182 183 bool ContentsView::IsShowingSearchResults() const { 184 return app_list::switches::IsExperimentalAppListEnabled() 185 ? IsNamedPageActive(NAMED_PAGE_START) && 186 start_page_view_->IsShowingSearchResults() 187 : IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS); 188 } 189 190 gfx::Rect ContentsView::GetOffscreenPageBounds(int page_index) const { 191 gfx::Rect bounds(GetContentsBounds()); 192 // The start page and search page origins are above; all other pages' origins 193 // are below. 194 int page_height = bounds.height(); 195 bool origin_above = 196 GetPageIndexForNamedPage(NAMED_PAGE_START) == page_index || 197 GetPageIndexForNamedPage(NAMED_PAGE_SEARCH_RESULTS) == page_index; 198 bounds.set_y(origin_above ? -page_height : page_height); 199 return bounds; 200 } 201 202 void ContentsView::UpdatePageBounds() { 203 // The bounds calculations will potentially be mid-transition (depending on 204 // the state of the PaginationModel). 205 int current_page = std::max(0, pagination_model_.selected_page()); 206 int target_page = current_page; 207 double progress = 1; 208 if (pagination_model_.has_transition()) { 209 const PaginationModel::Transition& transition = 210 pagination_model_.transition(); 211 if (pagination_model_.is_valid_page(transition.target_page)) { 212 target_page = transition.target_page; 213 progress = transition.progress; 214 } 215 } 216 217 // Move |current_page| from 0 to its origin. Move |target_page| from its 218 // origin to 0. 219 gfx::Rect on_screen(GetContentsBounds()); 220 gfx::Rect current_page_origin(GetOffscreenPageBounds(current_page)); 221 gfx::Rect target_page_origin(GetOffscreenPageBounds(target_page)); 222 gfx::Rect current_page_rect( 223 gfx::Tween::RectValueBetween(progress, on_screen, current_page_origin)); 224 gfx::Rect target_page_rect( 225 gfx::Tween::RectValueBetween(progress, target_page_origin, on_screen)); 226 227 view_model_->view_at(current_page)->SetBoundsRect(current_page_rect); 228 view_model_->view_at(target_page)->SetBoundsRect(target_page_rect); 229 } 230 231 PaginationModel* ContentsView::GetAppsPaginationModel() { 232 return apps_container_view_->apps_grid_view()->pagination_model(); 233 } 234 235 void ContentsView::ShowFolderContent(AppListFolderItem* item) { 236 apps_container_view_->ShowActiveFolder(item); 237 } 238 239 void ContentsView::Prerender() { 240 apps_container_view_->apps_grid_view()->Prerender(); 241 } 242 243 views::View* ContentsView::GetPageView(int index) { 244 return view_model_->view_at(index); 245 } 246 247 void ContentsView::AddBlankPageForTesting() { 248 AddLauncherPage(new views::View, 0); 249 } 250 251 int ContentsView::AddLauncherPage(views::View* view, int resource_id) { 252 int page_index = view_model_->view_size(); 253 AddChildView(view); 254 view_model_->Add(view, page_index); 255 if (contents_switcher_view_ && resource_id) 256 contents_switcher_view_->AddSwitcherButton(resource_id, page_index); 257 pagination_model_.SetTotalPages(view_model_->view_size()); 258 return page_index; 259 } 260 261 int ContentsView::AddLauncherPage(views::View* view, 262 int resource_id, 263 NamedPage named_page) { 264 int page_index = AddLauncherPage(view, resource_id); 265 named_page_to_view_.insert(std::pair<NamedPage, int>(named_page, page_index)); 266 return page_index; 267 } 268 269 gfx::Size ContentsView::GetPreferredSize() const { 270 const gfx::Size container_size = 271 apps_container_view_->apps_grid_view()->GetPreferredSize(); 272 const gfx::Size results_size = search_results_view_ 273 ? search_results_view_->GetPreferredSize() 274 : gfx::Size(); 275 276 int width = std::max(container_size.width(), results_size.width()); 277 int height = std::max(container_size.height(), results_size.height()); 278 return gfx::Size(width, height); 279 } 280 281 void ContentsView::Layout() { 282 // Immediately finish all current animations. 283 pagination_model_.FinishAnimation(); 284 285 // Move the current view onto the screen, and all other views off screen to 286 // the left. (Since we are not animating, we don't need to be careful about 287 // which side we place the off-screen views onto.) 288 gfx::Rect rect(GetContentsBounds()); 289 if (rect.IsEmpty()) 290 return; 291 292 gfx::Rect offscreen_target(rect); 293 offscreen_target.set_x(-rect.width()); 294 295 for (int i = 0; i < view_model_->view_size(); ++i) { 296 view_model_->view_at(i)->SetBoundsRect( 297 i == pagination_model_.SelectedTargetPage() ? rect : offscreen_target); 298 } 299 } 300 301 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) { 302 return view_model_->view_at(GetActivePageIndex())->OnKeyPressed(event); 303 } 304 305 void ContentsView::TotalPagesChanged() { 306 } 307 308 void ContentsView::SelectedPageChanged(int old_selected, int new_selected) { 309 } 310 311 void ContentsView::TransitionStarted() { 312 } 313 314 void ContentsView::TransitionChanged() { 315 UpdatePageBounds(); 316 } 317 318 } // namespace app_list 319