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/profiler/scoped_tracker.h"
14 #include "base/strings/string_util.h"
15 #include "ui/app_list/app_list_constants.h"
16 #include "ui/app_list/app_list_folder_item.h"
17 #include "ui/app_list/app_list_item.h"
18 #include "ui/app_list/app_list_model.h"
19 #include "ui/app_list/app_list_switches.h"
20 #include "ui/app_list/app_list_view_delegate.h"
21 #include "ui/app_list/pagination_model.h"
22 #include "ui/app_list/search_box_model.h"
23 #include "ui/app_list/views/app_list_folder_view.h"
24 #include "ui/app_list/views/app_list_item_view.h"
25 #include "ui/app_list/views/apps_container_view.h"
26 #include "ui/app_list/views/apps_grid_view.h"
27 #include "ui/app_list/views/contents_view.h"
28 #include "ui/app_list/views/custom_launcher_page_view.h"
29 #include "ui/app_list/views/search_box_view.h"
30 #include "ui/app_list/views/search_result_page_view.h"
31 #include "ui/app_list/views/start_page_view.h"
32 #include "ui/views/border.h"
33 #include "ui/views/controls/button/button.h"
34 #include "ui/views/controls/button/custom_button.h"
35 #include "ui/views/controls/textfield/textfield.h"
36 #include "ui/views/layout/box_layout.h"
37 #include "ui/views/layout/fill_layout.h"
38 #include "ui/views/widget/widget.h"
44 // The maximum allowed time to wait for icon loading in milliseconds.
45 const int kMaxIconLoadingWaitTimeInMs
= 50;
49 ////////////////////////////////////////////////////////////////////////////////
50 // AppListMainView::IconLoader
52 class AppListMainView::IconLoader
: public AppListItemObserver
{
54 IconLoader(AppListMainView
* owner
,
59 item_
->AddObserver(this);
61 // Triggers icon loading for given |scale_factor|.
62 item_
->icon().GetRepresentation(scale
);
65 ~IconLoader() override
{ item_
->RemoveObserver(this); }
68 // AppListItemObserver overrides:
69 void ItemIconChanged() override
{
70 owner_
->OnItemIconLoaded(this);
71 // Note that IconLoader is released here.
74 AppListMainView
* owner_
;
77 DISALLOW_COPY_AND_ASSIGN(IconLoader
);
80 ////////////////////////////////////////////////////////////////////////////////
83 AppListMainView::AppListMainView(AppListViewDelegate
* delegate
)
84 : delegate_(delegate
),
85 model_(delegate
->GetModel()),
86 search_box_view_(nullptr),
87 contents_view_(nullptr),
88 weak_ptr_factory_(this) {
89 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
, 0, 0, 0));
90 model_
->AddObserver(this);
93 AppListMainView::~AppListMainView() {
94 pending_icon_loaders_
.clear();
95 model_
->RemoveObserver(this);
98 void AppListMainView::Init(gfx::NativeView parent
,
99 int initial_apps_page
,
100 SearchBoxView
* search_box_view
) {
101 search_box_view_
= search_box_view
;
104 // Switch the apps grid view to the specified page.
105 app_list::PaginationModel
* pagination_model
= GetAppsPaginationModel();
106 if (pagination_model
->is_valid_page(initial_apps_page
))
107 pagination_model
->SelectPage(initial_apps_page
, false);
109 // Starts icon loading early.
110 PreloadIcons(parent
);
112 OnSearchEngineIsGoogleChanged(model_
->search_engine_is_google());
115 void AppListMainView::AddContentsViews() {
116 DCHECK(search_box_view_
);
118 contents_view_
= new ContentsView(this);
119 contents_view_
->Init(model_
);
120 AddChildView(contents_view_
);
122 search_box_view_
->set_contents_view(contents_view_
);
124 contents_view_
->SetPaintToLayer(true);
125 contents_view_
->SetFillsBoundsOpaquely(false);
126 contents_view_
->layer()->SetMasksToBounds(true);
128 delegate_
->StartSearch();
131 void AppListMainView::ShowAppListWhenReady() {
132 if (pending_icon_loaders_
.empty()) {
133 icon_loading_wait_timer_
.Stop();
138 if (icon_loading_wait_timer_
.IsRunning())
141 icon_loading_wait_timer_
.Start(
143 base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs
),
144 this, &AppListMainView::OnIconLoadingWaitTimer
);
147 void AppListMainView::ResetForShow() {
148 if (switches::IsExperimentalAppListEnabled())
149 contents_view_
->SetActiveState(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_
->RemoveObserver(this);
169 model_
= delegate_
->GetModel();
170 model_
->AddObserver(this);
171 search_box_view_
->ModelChanged();
172 delete contents_view_
;
173 contents_view_
= NULL
;
178 void AppListMainView::SetDragAndDropHostOfCurrentAppList(
179 ApplicationDragAndDropHost
* drag_and_drop_host
) {
180 contents_view_
->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host
);
183 bool AppListMainView::ShouldCenterWindow() const {
184 return delegate_
->ShouldCenterWindow();
187 PaginationModel
* AppListMainView::GetAppsPaginationModel() {
188 return contents_view_
->apps_container_view()
190 ->pagination_model();
193 void AppListMainView::PreloadIcons(gfx::NativeView parent
) {
194 float scale_factor
= 1.0f
;
196 scale_factor
= ui::GetScaleFactorForNativeView(parent
);
198 // The PaginationModel could have -1 as the initial selected page and
199 // assumes first page (i.e. index 0) will be used in this case.
200 const int selected_page
=
201 std::max(0, GetAppsPaginationModel()->selected_page());
203 const AppsGridView
* const apps_grid_view
=
204 contents_view_
->apps_container_view()->apps_grid_view();
205 const int tiles_per_page
=
206 apps_grid_view
->cols() * apps_grid_view
->rows_per_page();
208 const int start_model_index
= selected_page
* tiles_per_page
;
209 const int end_model_index
=
210 std::min(static_cast<int>(model_
->top_level_item_list()->item_count()),
211 start_model_index
+ tiles_per_page
);
213 pending_icon_loaders_
.clear();
214 for (int i
= start_model_index
; i
< end_model_index
; ++i
) {
215 AppListItem
* item
= model_
->top_level_item_list()->item_at(i
);
216 if (item
->icon().HasRepresentation(scale_factor
))
219 pending_icon_loaders_
.push_back(new IconLoader(this, item
, scale_factor
));
223 void AppListMainView::OnIconLoadingWaitTimer() {
227 void AppListMainView::OnItemIconLoaded(IconLoader
* loader
) {
228 ScopedVector
<IconLoader
>::iterator it
= std::find(
229 pending_icon_loaders_
.begin(), pending_icon_loaders_
.end(), loader
);
230 DCHECK(it
!= pending_icon_loaders_
.end());
231 pending_icon_loaders_
.erase(it
);
233 if (pending_icon_loaders_
.empty() && icon_loading_wait_timer_
.IsRunning()) {
234 icon_loading_wait_timer_
.Stop();
239 void AppListMainView::NotifySearchBoxVisibilityChanged() {
240 // Repaint the AppListView's background which will repaint the background for
241 // the search box. This is needed because this view paints to a layer and
242 // won't propagate paints upward.
244 parent()->SchedulePaint();
247 bool AppListMainView::ShouldShowCustomLauncherPage() const {
248 return contents_view_
->custom_page_view() &&
249 model_
->custom_launcher_page_enabled() &&
250 model_
->search_engine_is_google();
253 void AppListMainView::UpdateCustomLauncherPageVisibility() {
254 views::View
* custom_page
= contents_view_
->custom_page_view();
258 if (ShouldShowCustomLauncherPage()) {
259 // Make the custom page view visible again.
260 custom_page
->SetVisible(true);
261 } else if (contents_view_
->IsStateActive(
262 AppListModel::STATE_CUSTOM_LAUNCHER_PAGE
)) {
263 // Animate to the start page if currently on the custom page view. The view
264 // will hide on animation completion.
265 contents_view_
->SetActiveState(AppListModel::STATE_START
);
267 // Hide the view immediately otherwise.
268 custom_page
->SetVisible(false);
272 void AppListMainView::OnCustomLauncherPageEnabledStateChanged(bool enabled
) {
273 UpdateCustomLauncherPageVisibility();
276 void AppListMainView::OnSearchEngineIsGoogleChanged(bool is_google
) {
277 if (contents_view_
->custom_page_view())
278 UpdateCustomLauncherPageVisibility();
280 if (contents_view_
->start_page_view()) {
281 contents_view_
->start_page_view()->instant_container()->SetVisible(
286 void AppListMainView::ActivateApp(AppListItem
* item
, int event_flags
) {
287 // TODO(jennyz): Activate the folder via AppListModel notification.
288 if (item
->GetItemType() == AppListFolderItem::kItemType
)
289 contents_view_
->ShowFolderContent(static_cast<AppListFolderItem
*>(item
));
291 item
->Activate(event_flags
);
294 void AppListMainView::GetShortcutPathForApp(
295 const std::string
& app_id
,
296 const base::Callback
<void(const base::FilePath
&)>& callback
) {
297 delegate_
->GetShortcutPathForApp(app_id
, callback
);
300 void AppListMainView::CancelDragInActiveFolder() {
301 contents_view_
->apps_container_view()
302 ->app_list_folder_view()
307 void AppListMainView::QueryChanged(SearchBoxView
* sender
) {
308 base::string16 query
;
309 base::TrimWhitespace(model_
->search_box()->text(), base::TRIM_ALL
, &query
);
310 bool should_show_search
= !query
.empty();
311 contents_view_
->ShowSearchResults(should_show_search
);
313 delegate_
->StartSearch();
316 void AppListMainView::BackButtonPressed() {
317 contents_view_
->Back();
320 void AppListMainView::SetSearchResultSelection(bool select
) {
321 if (contents_view_
->GetActiveState() == AppListModel::STATE_SEARCH_RESULTS
)
322 contents_view_
->search_results_page_view()->SetSelection(select
);
325 void AppListMainView::OnResultInstalled(SearchResult
* result
) {
326 // Clears the search to show the apps grid. The last installed app
327 // should be highlighted and made visible already.
328 search_box_view_
->ClearSearch();
331 } // namespace app_list