1 // Copyright 2013 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/app_list_main_view.h"
10 #include "base/callback.h"
11 #include "base/files/file_path.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/string_util.h"
14 #include "ui/app_list/app_list_constants.h"
15 #include "ui/app_list/app_list_folder_item.h"
16 #include "ui/app_list/app_list_item.h"
17 #include "ui/app_list/app_list_model.h"
18 #include "ui/app_list/app_list_switches.h"
19 #include "ui/app_list/app_list_view_delegate.h"
20 #include "ui/app_list/pagination_model.h"
21 #include "ui/app_list/search_box_model.h"
22 #include "ui/app_list/views/app_list_folder_view.h"
23 #include "ui/app_list/views/app_list_item_view.h"
24 #include "ui/app_list/views/apps_container_view.h"
25 #include "ui/app_list/views/apps_grid_view.h"
26 #include "ui/app_list/views/contents_switcher_view.h"
27 #include "ui/app_list/views/contents_view.h"
28 #include "ui/app_list/views/search_box_view.h"
29 #include "ui/views/border.h"
30 #include "ui/views/controls/textfield/textfield.h"
31 #include "ui/views/layout/box_layout.h"
32 #include "ui/views/layout/fill_layout.h"
33 #include "ui/views/widget/widget.h"
39 // The maximum allowed time to wait for icon loading in milliseconds.
40 const int kMaxIconLoadingWaitTimeInMs = 50;
44 ////////////////////////////////////////////////////////////////////////////////
45 // AppListMainView::IconLoader
47 class AppListMainView::IconLoader : public AppListItemObserver {
49 IconLoader(AppListMainView* owner,
54 item_->AddObserver(this);
56 // Triggers icon loading for given |scale_factor|.
57 item_->icon().GetRepresentation(scale);
60 ~IconLoader() override { item_->RemoveObserver(this); }
63 // AppListItemObserver overrides:
64 void ItemIconChanged() override {
65 owner_->OnItemIconLoaded(this);
66 // Note that IconLoader is released here.
69 AppListMainView* owner_;
72 DISALLOW_COPY_AND_ASSIGN(IconLoader);
75 ////////////////////////////////////////////////////////////////////////////////
78 AppListMainView::AppListMainView(AppListViewDelegate* delegate)
79 : delegate_(delegate),
80 model_(delegate->GetModel()),
81 search_box_view_(NULL),
83 contents_switcher_view_(NULL),
84 weak_ptr_factory_(this) {
85 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
88 AppListMainView::~AppListMainView() {
89 pending_icon_loaders_.clear();
92 void AppListMainView::Init(gfx::NativeView parent,
93 int initial_apps_page,
94 SearchBoxView* search_box_view) {
95 search_box_view_ = search_box_view;
98 // Switch the apps grid view to the specified page.
99 app_list::PaginationModel* pagination_model = GetAppsPaginationModel();
100 if (pagination_model->is_valid_page(initial_apps_page))
101 pagination_model->SelectPage(initial_apps_page, false);
103 // Starts icon loading early.
104 PreloadIcons(parent);
107 void AppListMainView::AddContentsViews() {
108 DCHECK(search_box_view_);
110 contents_view_ = new ContentsView(this);
111 if (app_list::switches::IsExperimentalAppListEnabled()) {
112 contents_switcher_view_ = new ContentsSwitcherView(contents_view_);
113 contents_view_->SetContentsSwitcherView(contents_switcher_view_);
115 contents_view_->Init(model_, delegate_);
116 AddChildView(contents_view_);
117 if (contents_switcher_view_)
118 AddChildView(contents_switcher_view_);
120 search_box_view_->set_contents_view(contents_view_);
121 UpdateSearchBoxVisibility();
123 contents_view_->SetPaintToLayer(true);
124 contents_view_->SetFillsBoundsOpaquely(false);
125 contents_view_->layer()->SetMasksToBounds(true);
127 delegate_->StartSearch();
130 void AppListMainView::ShowAppListWhenReady() {
131 if (pending_icon_loaders_.empty()) {
132 icon_loading_wait_timer_.Stop();
137 if (icon_loading_wait_timer_.IsRunning())
140 icon_loading_wait_timer_.Start(
142 base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs),
143 this, &AppListMainView::OnIconLoadingWaitTimer);
146 void AppListMainView::ResetForShow() {
147 if (switches::IsExperimentalAppListEnabled()) {
148 contents_view_->SetActivePage(
149 contents_view_->GetPageIndexForState(AppListModel::STATE_START));
151 contents_view_->apps_container_view()->ResetForShowApps();
152 // We clear the search when hiding so when app list appears it is not showing
154 search_box_view_->ClearSearch();
157 void AppListMainView::Close() {
158 icon_loading_wait_timer_.Stop();
159 contents_view_->CancelDrag();
162 void AppListMainView::Prerender() {
163 contents_view_->Prerender();
166 void AppListMainView::ModelChanged() {
167 pending_icon_loaders_.clear();
168 model_ = delegate_->GetModel();
169 search_box_view_->ModelChanged();
170 delete contents_view_;
171 contents_view_ = NULL;
172 if (contents_switcher_view_) {
173 delete contents_switcher_view_;
174 contents_switcher_view_ = NULL;
180 void AppListMainView::UpdateSearchBoxVisibility() {
181 bool visible = !contents_view_->IsStateActive(AppListModel::STATE_START) ||
182 contents_view_->IsShowingSearchResults();
183 search_box_view_->SetVisible(visible);
184 if (visible && GetWidget() && GetWidget()->IsVisible())
185 search_box_view_->search_box()->RequestFocus();
188 void AppListMainView::OnStartPageSearchTextfieldChanged(
189 const base::string16& new_contents) {
190 search_box_view_->SetVisible(true);
191 search_box_view_->search_box()->SetText(new_contents);
192 search_box_view_->search_box()->RequestFocus();
195 void AppListMainView::SetDragAndDropHostOfCurrentAppList(
196 ApplicationDragAndDropHost* drag_and_drop_host) {
197 contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
200 bool AppListMainView::ShouldCenterWindow() const {
201 return delegate_->ShouldCenterWindow();
204 PaginationModel* AppListMainView::GetAppsPaginationModel() {
205 return contents_view_->apps_container_view()
207 ->pagination_model();
210 void AppListMainView::PreloadIcons(gfx::NativeView parent) {
211 float scale_factor = 1.0f;
213 scale_factor = ui::GetScaleFactorForNativeView(parent);
215 // The PaginationModel could have -1 as the initial selected page and
216 // assumes first page (i.e. index 0) will be used in this case.
217 const int selected_page =
218 std::max(0, GetAppsPaginationModel()->selected_page());
220 const AppsGridView* const apps_grid_view =
221 contents_view_->apps_container_view()->apps_grid_view();
222 const int tiles_per_page =
223 apps_grid_view->cols() * apps_grid_view->rows_per_page();
225 const int start_model_index = selected_page * tiles_per_page;
226 const int end_model_index =
227 std::min(static_cast<int>(model_->top_level_item_list()->item_count()),
228 start_model_index + tiles_per_page);
230 pending_icon_loaders_.clear();
231 for (int i = start_model_index; i < end_model_index; ++i) {
232 AppListItem* item = model_->top_level_item_list()->item_at(i);
233 if (item->icon().HasRepresentation(scale_factor))
236 pending_icon_loaders_.push_back(new IconLoader(this, item, scale_factor));
240 void AppListMainView::OnIconLoadingWaitTimer() {
244 void AppListMainView::OnItemIconLoaded(IconLoader* loader) {
245 ScopedVector<IconLoader>::iterator it = std::find(
246 pending_icon_loaders_.begin(), pending_icon_loaders_.end(), loader);
247 DCHECK(it != pending_icon_loaders_.end());
248 pending_icon_loaders_.erase(it);
250 if (pending_icon_loaders_.empty() && icon_loading_wait_timer_.IsRunning()) {
251 icon_loading_wait_timer_.Stop();
256 void AppListMainView::NotifySearchBoxVisibilityChanged() {
257 // Repaint the AppListView's background which will repaint the background for
258 // the search box. This is needed because this view paints to a layer and
259 // won't propagate paints upward.
261 parent()->SchedulePaint();
264 void AppListMainView::ActivateApp(AppListItem* item, int event_flags) {
265 // TODO(jennyz): Activate the folder via AppListModel notification.
266 if (item->GetItemType() == AppListFolderItem::kItemType)
267 contents_view_->ShowFolderContent(static_cast<AppListFolderItem*>(item));
269 item->Activate(event_flags);
272 void AppListMainView::GetShortcutPathForApp(
273 const std::string& app_id,
274 const base::Callback<void(const base::FilePath&)>& callback) {
275 delegate_->GetShortcutPathForApp(app_id, callback);
278 void AppListMainView::CancelDragInActiveFolder() {
279 contents_view_->apps_container_view()
280 ->app_list_folder_view()
285 void AppListMainView::QueryChanged(SearchBoxView* sender) {
286 base::string16 query;
287 base::TrimWhitespace(model_->search_box()->text(), base::TRIM_ALL, &query);
288 bool should_show_search = !query.empty();
289 contents_view_->ShowSearchResults(should_show_search);
290 UpdateSearchBoxVisibility();
292 delegate_->StartSearch();
295 void AppListMainView::OnResultInstalled(SearchResult* result) {
296 // Clears the search to show the apps grid. The last installed app
297 // should be highlighted and made visible already.
298 search_box_view_->ClearSearch();
301 void AppListMainView::OnResultUninstalled(SearchResult* result) {
302 // Resubmit the query via a posted task so that all observers for the
303 // uninstall notification are notified.
304 base::MessageLoop::current()->PostTask(
306 base::Bind(&AppListMainView::QueryChanged,
307 weak_ptr_factory_.GetWeakPtr(),
311 } // namespace app_list