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 // Inner padding space in pixels of bubble contents.
40 const int kInnerPadding
= 1;
42 // The maximum allowed time to wait for icon loading in milliseconds.
43 const int kMaxIconLoadingWaitTimeInMs
= 50;
45 // The padding around the search box in the experimental app list.
46 const int kSearchBoxViewPadding
= 24;
47 const int kSearchBoxViewPaddingBottom
= 12;
49 // A view that holds another view and takes its preferred size. This is used for
50 // wrapping the search box view so it still gets laid out while hidden. This is
51 // a separate class so it can notify the main view on search box visibility
53 class SearchBoxContainerView
: public views::View
{
55 SearchBoxContainerView(AppListMainView
* host
, SearchBoxView
* search_box
)
56 : host_(host
), search_box_(search_box
) {
57 SetLayoutManager(new views::FillLayout());
58 AddChildView(search_box
);
60 virtual ~SearchBoxContainerView() {}
63 // Overridden from views::View:
64 virtual void ChildVisibilityChanged(views::View
* child
) OVERRIDE
{
65 DCHECK_EQ(search_box_
, child
);
66 host_
->NotifySearchBoxVisibilityChanged();
69 AppListMainView
* host_
;
70 SearchBoxView
* search_box_
;
72 DISALLOW_COPY_AND_ASSIGN(SearchBoxContainerView
);
77 ////////////////////////////////////////////////////////////////////////////////
78 // AppListMainView::IconLoader
80 class AppListMainView::IconLoader
: public AppListItemObserver
{
82 IconLoader(AppListMainView
* owner
,
87 item_
->AddObserver(this);
89 // Triggers icon loading for given |scale_factor|.
90 item_
->icon().GetRepresentation(scale
);
93 virtual ~IconLoader() {
94 item_
->RemoveObserver(this);
98 // AppListItemObserver overrides:
99 virtual void ItemIconChanged() OVERRIDE
{
100 owner_
->OnItemIconLoaded(this);
101 // Note that IconLoader is released here.
104 AppListMainView
* owner_
;
107 DISALLOW_COPY_AND_ASSIGN(IconLoader
);
110 ////////////////////////////////////////////////////////////////////////////////
113 AppListMainView::AppListMainView(AppListViewDelegate
* delegate
,
114 int initial_apps_page
,
115 gfx::NativeView parent
)
116 : delegate_(delegate
),
117 model_(delegate
->GetModel()),
118 search_box_view_(NULL
),
119 contents_view_(NULL
),
120 contents_switcher_view_(NULL
),
121 weak_ptr_factory_(this) {
122 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical
,
127 search_box_view_
= new SearchBoxView(this, delegate
);
128 views::View
* container
= new SearchBoxContainerView(this, search_box_view_
);
129 if (switches::IsExperimentalAppListEnabled()) {
130 container
->SetBorder(
131 views::Border::CreateEmptyBorder(kSearchBoxViewPadding
,
132 kSearchBoxViewPadding
,
133 kSearchBoxViewPaddingBottom
,
134 kSearchBoxViewPadding
));
136 AddChildView(container
);
139 // Switch the apps grid view to the specified page.
140 app_list::PaginationModel
* pagination_model
= GetAppsPaginationModel();
141 if (pagination_model
->is_valid_page(initial_apps_page
))
142 pagination_model
->SelectPage(initial_apps_page
, false);
144 // Starts icon loading early.
145 PreloadIcons(parent
);
148 void AppListMainView::AddContentsViews() {
149 contents_view_
= new ContentsView(this);
150 if (app_list::switches::IsExperimentalAppListEnabled()) {
151 contents_switcher_view_
= new ContentsSwitcherView(contents_view_
);
152 contents_view_
->SetContentsSwitcherView(contents_switcher_view_
);
154 contents_view_
->InitNamedPages(model_
, delegate_
);
155 AddChildView(contents_view_
);
156 if (contents_switcher_view_
)
157 AddChildView(contents_switcher_view_
);
159 search_box_view_
->set_contents_view(contents_view_
);
161 contents_view_
->SetPaintToLayer(true);
162 contents_view_
->SetFillsBoundsOpaquely(false);
163 contents_view_
->layer()->SetMasksToBounds(true);
165 delegate_
->StartSearch();
168 AppListMainView::~AppListMainView() {
169 pending_icon_loaders_
.clear();
172 void AppListMainView::ShowAppListWhenReady() {
173 if (pending_icon_loaders_
.empty()) {
174 icon_loading_wait_timer_
.Stop();
179 if (icon_loading_wait_timer_
.IsRunning())
182 icon_loading_wait_timer_
.Start(
184 base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs
),
185 this, &AppListMainView::OnIconLoadingWaitTimer
);
188 void AppListMainView::ResetForShow() {
189 contents_view_
->apps_container_view()->ResetForShowApps();
190 // We clear the search when hiding so when app list appears it is not showing
192 search_box_view_
->ClearSearch();
195 void AppListMainView::Close() {
196 icon_loading_wait_timer_
.Stop();
197 contents_view_
->CancelDrag();
200 void AppListMainView::Prerender() {
201 contents_view_
->Prerender();
204 void AppListMainView::ModelChanged() {
205 pending_icon_loaders_
.clear();
206 model_
= delegate_
->GetModel();
207 search_box_view_
->ModelChanged();
208 delete contents_view_
;
209 contents_view_
= NULL
;
210 if (contents_switcher_view_
) {
211 delete contents_switcher_view_
;
212 contents_switcher_view_
= NULL
;
218 void AppListMainView::UpdateSearchBoxVisibility() {
220 !contents_view_
->IsNamedPageActive(ContentsView::NAMED_PAGE_START
) ||
221 contents_view_
->IsShowingSearchResults();
222 search_box_view_
->SetVisible(visible
);
223 if (visible
&& GetWidget() && GetWidget()->IsVisible())
224 search_box_view_
->search_box()->RequestFocus();
227 void AppListMainView::OnStartPageSearchTextfieldChanged(
228 const base::string16
& new_contents
) {
229 search_box_view_
->SetVisible(true);
230 search_box_view_
->search_box()->SetText(new_contents
);
231 search_box_view_
->search_box()->RequestFocus();
234 void AppListMainView::SetDragAndDropHostOfCurrentAppList(
235 ApplicationDragAndDropHost
* drag_and_drop_host
) {
236 contents_view_
->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host
);
239 bool AppListMainView::ShouldCenterWindow() const {
240 return delegate_
->ShouldCenterWindow();
243 PaginationModel
* AppListMainView::GetAppsPaginationModel() {
244 return contents_view_
->apps_container_view()
246 ->pagination_model();
249 void AppListMainView::PreloadIcons(gfx::NativeView parent
) {
250 float scale_factor
= 1.0f
;
252 scale_factor
= ui::GetScaleFactorForNativeView(parent
);
254 // The PaginationModel could have -1 as the initial selected page and
255 // assumes first page (i.e. index 0) will be used in this case.
256 const int selected_page
=
257 std::max(0, GetAppsPaginationModel()->selected_page());
259 const AppsGridView
* const apps_grid_view
=
260 contents_view_
->apps_container_view()->apps_grid_view();
261 const int tiles_per_page
=
262 apps_grid_view
->cols() * apps_grid_view
->rows_per_page();
264 const int start_model_index
= selected_page
* tiles_per_page
;
265 const int end_model_index
=
266 std::min(static_cast<int>(model_
->top_level_item_list()->item_count()),
267 start_model_index
+ tiles_per_page
);
269 pending_icon_loaders_
.clear();
270 for (int i
= start_model_index
; i
< end_model_index
; ++i
) {
271 AppListItem
* item
= model_
->top_level_item_list()->item_at(i
);
272 if (item
->icon().HasRepresentation(scale_factor
))
275 pending_icon_loaders_
.push_back(new IconLoader(this, item
, scale_factor
));
279 void AppListMainView::OnIconLoadingWaitTimer() {
283 void AppListMainView::OnItemIconLoaded(IconLoader
* loader
) {
284 ScopedVector
<IconLoader
>::iterator it
= std::find(
285 pending_icon_loaders_
.begin(), pending_icon_loaders_
.end(), loader
);
286 DCHECK(it
!= pending_icon_loaders_
.end());
287 pending_icon_loaders_
.erase(it
);
289 if (pending_icon_loaders_
.empty() && icon_loading_wait_timer_
.IsRunning()) {
290 icon_loading_wait_timer_
.Stop();
295 void AppListMainView::NotifySearchBoxVisibilityChanged() {
296 // Repaint the AppListView's background which will repaint the background for
297 // the search box. This is needed because this view paints to a layer and
298 // won't propagate paints upward.
300 parent()->SchedulePaint();
303 void AppListMainView::ActivateApp(AppListItem
* item
, int event_flags
) {
304 // TODO(jennyz): Activate the folder via AppListModel notification.
305 if (item
->GetItemType() == AppListFolderItem::kItemType
)
306 contents_view_
->ShowFolderContent(static_cast<AppListFolderItem
*>(item
));
308 item
->Activate(event_flags
);
311 void AppListMainView::GetShortcutPathForApp(
312 const std::string
& app_id
,
313 const base::Callback
<void(const base::FilePath
&)>& callback
) {
314 delegate_
->GetShortcutPathForApp(app_id
, callback
);
317 void AppListMainView::CancelDragInActiveFolder() {
318 contents_view_
->apps_container_view()
319 ->app_list_folder_view()
324 void AppListMainView::QueryChanged(SearchBoxView
* sender
) {
325 base::string16 query
;
326 base::TrimWhitespace(model_
->search_box()->text(), base::TRIM_ALL
, &query
);
327 bool should_show_search
= !query
.empty();
328 contents_view_
->ShowSearchResults(should_show_search
);
329 UpdateSearchBoxVisibility();
331 delegate_
->StartSearch();
334 void AppListMainView::OnResultInstalled(SearchResult
* result
) {
335 // Clears the search to show the apps grid. The last installed app
336 // should be highlighted and made visible already.
337 search_box_view_
->ClearSearch();
340 void AppListMainView::OnResultUninstalled(SearchResult
* result
) {
341 // Resubmit the query via a posted task so that all observers for the
342 // uninstall notification are notified.
343 base::MessageLoop::current()->PostTask(
345 base::Bind(&AppListMainView::QueryChanged
,
346 weak_ptr_factory_
.GetWeakPtr(),
350 } // namespace app_list