Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ui / app_list / views / app_list_main_view.cc
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.
4
5 #include "ui/app_list/views/app_list_main_view.h"
6
7 #include <algorithm>
8
9 #include "base/bind.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"
34
35 namespace app_list {
36
37 namespace {
38
39 // Border padding space around the bubble contents.
40 const int kPadding = 1;
41
42 // The maximum allowed time to wait for icon loading in milliseconds.
43 const int kMaxIconLoadingWaitTimeInMs = 50;
44
45 // A view that holds another view and takes its preferred size. This is used for
46 // wrapping the search box view so it still gets laid out while hidden. This is
47 // a separate class so it can notify the main view on search box visibility
48 // change.
49 class SearchBoxContainerView : public views::View {
50  public:
51   SearchBoxContainerView(AppListMainView* host, SearchBoxView* search_box)
52       : host_(host), search_box_(search_box) {
53     SetLayoutManager(new views::FillLayout());
54     AddChildView(search_box);
55   }
56   virtual ~SearchBoxContainerView() {}
57
58  private:
59   // Overridden from views::View:
60   virtual void ChildVisibilityChanged(views::View* child) OVERRIDE {
61     DCHECK_EQ(search_box_, child);
62     host_->NotifySearchBoxVisibilityChanged();
63   }
64
65   AppListMainView* host_;
66   SearchBoxView* search_box_;
67
68   DISALLOW_COPY_AND_ASSIGN(SearchBoxContainerView);
69 };
70
71 }  // namespace
72
73 ////////////////////////////////////////////////////////////////////////////////
74 // AppListMainView::IconLoader
75
76 class AppListMainView::IconLoader : public AppListItemObserver {
77  public:
78   IconLoader(AppListMainView* owner,
79              AppListItem* item,
80              float scale)
81       : owner_(owner),
82         item_(item) {
83     item_->AddObserver(this);
84
85     // Triggers icon loading for given |scale_factor|.
86     item_->icon().GetRepresentation(scale);
87   }
88
89   virtual ~IconLoader() {
90     item_->RemoveObserver(this);
91   }
92
93  private:
94   // AppListItemObserver overrides:
95   virtual void ItemIconChanged() OVERRIDE {
96     owner_->OnItemIconLoaded(this);
97     // Note that IconLoader is released here.
98   }
99
100   AppListMainView* owner_;
101   AppListItem* item_;
102
103   DISALLOW_COPY_AND_ASSIGN(IconLoader);
104 };
105
106 ////////////////////////////////////////////////////////////////////////////////
107 // AppListMainView:
108
109 AppListMainView::AppListMainView(AppListViewDelegate* delegate,
110                                  int initial_apps_page,
111                                  gfx::NativeView parent)
112     : delegate_(delegate),
113       model_(delegate->GetModel()),
114       search_box_view_(NULL),
115       contents_view_(NULL),
116       contents_switcher_view_(NULL),
117       weak_ptr_factory_(this) {
118   SetBorder(
119       views::Border::CreateEmptyBorder(kPadding, kPadding, kPadding, kPadding));
120   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
121
122   search_box_view_ = new SearchBoxView(this, delegate);
123   views::View* container = new SearchBoxContainerView(this, search_box_view_);
124   if (switches::IsExperimentalAppListEnabled()) {
125     container->SetBorder(
126         views::Border::CreateEmptyBorder(kExperimentalWindowPadding,
127                                          kExperimentalWindowPadding,
128                                          0,
129                                          kExperimentalWindowPadding));
130   }
131   AddChildView(container);
132   AddContentsViews();
133
134   // Switch the apps grid view to the specified page.
135   app_list::PaginationModel* pagination_model = GetAppsPaginationModel();
136   if (pagination_model->is_valid_page(initial_apps_page))
137     pagination_model->SelectPage(initial_apps_page, false);
138
139   // Starts icon loading early.
140   PreloadIcons(parent);
141 }
142
143 void AppListMainView::AddContentsViews() {
144   contents_view_ = new ContentsView(this);
145   if (app_list::switches::IsExperimentalAppListEnabled()) {
146     contents_switcher_view_ = new ContentsSwitcherView(contents_view_);
147     contents_view_->SetContentsSwitcherView(contents_switcher_view_);
148   }
149   contents_view_->InitNamedPages(model_, delegate_);
150   AddChildView(contents_view_);
151   if (contents_switcher_view_)
152     AddChildView(contents_switcher_view_);
153
154   search_box_view_->set_contents_view(contents_view_);
155
156   contents_view_->SetPaintToLayer(true);
157   contents_view_->SetFillsBoundsOpaquely(false);
158   contents_view_->layer()->SetMasksToBounds(true);
159
160   delegate_->StartSearch();
161 }
162
163 AppListMainView::~AppListMainView() {
164   pending_icon_loaders_.clear();
165 }
166
167 void AppListMainView::ShowAppListWhenReady() {
168   if (pending_icon_loaders_.empty()) {
169     icon_loading_wait_timer_.Stop();
170     GetWidget()->Show();
171     return;
172   }
173
174   if (icon_loading_wait_timer_.IsRunning())
175     return;
176
177   icon_loading_wait_timer_.Start(
178       FROM_HERE,
179       base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs),
180       this, &AppListMainView::OnIconLoadingWaitTimer);
181 }
182
183 void AppListMainView::ResetForShow() {
184   if (switches::IsExperimentalAppListEnabled()) {
185     contents_view_->SetActivePage(contents_view_->GetPageIndexForNamedPage(
186         ContentsView::NAMED_PAGE_START));
187   }
188   contents_view_->apps_container_view()->ResetForShowApps();
189   // We clear the search when hiding so when app list appears it is not showing
190   // search results.
191   search_box_view_->ClearSearch();
192 }
193
194 void AppListMainView::Close() {
195   icon_loading_wait_timer_.Stop();
196   contents_view_->CancelDrag();
197 }
198
199 void AppListMainView::Prerender() {
200   contents_view_->Prerender();
201 }
202
203 void AppListMainView::ModelChanged() {
204   pending_icon_loaders_.clear();
205   model_ = delegate_->GetModel();
206   search_box_view_->ModelChanged();
207   delete contents_view_;
208   contents_view_ = NULL;
209   if (contents_switcher_view_) {
210     delete contents_switcher_view_;
211     contents_switcher_view_ = NULL;
212   }
213   AddContentsViews();
214   Layout();
215 }
216
217 void AppListMainView::UpdateSearchBoxVisibility() {
218   bool visible =
219       !contents_view_->IsNamedPageActive(ContentsView::NAMED_PAGE_START) ||
220       contents_view_->IsShowingSearchResults();
221   search_box_view_->SetVisible(visible);
222   if (visible && GetWidget() && GetWidget()->IsVisible())
223     search_box_view_->search_box()->RequestFocus();
224 }
225
226 void AppListMainView::OnStartPageSearchTextfieldChanged(
227     const base::string16& new_contents) {
228   search_box_view_->SetVisible(true);
229   search_box_view_->search_box()->SetText(new_contents);
230   search_box_view_->search_box()->RequestFocus();
231 }
232
233 void AppListMainView::SetDragAndDropHostOfCurrentAppList(
234     ApplicationDragAndDropHost* drag_and_drop_host) {
235   contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
236 }
237
238 bool AppListMainView::ShouldCenterWindow() const {
239   return delegate_->ShouldCenterWindow();
240 }
241
242 PaginationModel* AppListMainView::GetAppsPaginationModel() {
243   return contents_view_->apps_container_view()
244       ->apps_grid_view()
245       ->pagination_model();
246 }
247
248 void AppListMainView::PreloadIcons(gfx::NativeView parent) {
249   float scale_factor = 1.0f;
250   if (parent)
251     scale_factor = ui::GetScaleFactorForNativeView(parent);
252
253   // The PaginationModel could have -1 as the initial selected page and
254   // assumes first page (i.e. index 0) will be used in this case.
255   const int selected_page =
256       std::max(0, GetAppsPaginationModel()->selected_page());
257
258   const AppsGridView* const apps_grid_view =
259       contents_view_->apps_container_view()->apps_grid_view();
260   const int tiles_per_page =
261       apps_grid_view->cols() * apps_grid_view->rows_per_page();
262
263   const int start_model_index = selected_page * tiles_per_page;
264   const int end_model_index =
265       std::min(static_cast<int>(model_->top_level_item_list()->item_count()),
266                start_model_index + tiles_per_page);
267
268   pending_icon_loaders_.clear();
269   for (int i = start_model_index; i < end_model_index; ++i) {
270     AppListItem* item = model_->top_level_item_list()->item_at(i);
271     if (item->icon().HasRepresentation(scale_factor))
272       continue;
273
274     pending_icon_loaders_.push_back(new IconLoader(this, item, scale_factor));
275   }
276 }
277
278 void AppListMainView::OnIconLoadingWaitTimer() {
279   GetWidget()->Show();
280 }
281
282 void AppListMainView::OnItemIconLoaded(IconLoader* loader) {
283   ScopedVector<IconLoader>::iterator it = std::find(
284       pending_icon_loaders_.begin(), pending_icon_loaders_.end(), loader);
285   DCHECK(it != pending_icon_loaders_.end());
286   pending_icon_loaders_.erase(it);
287
288   if (pending_icon_loaders_.empty() && icon_loading_wait_timer_.IsRunning()) {
289     icon_loading_wait_timer_.Stop();
290     GetWidget()->Show();
291   }
292 }
293
294 void AppListMainView::NotifySearchBoxVisibilityChanged() {
295   // Repaint the AppListView's background which will repaint the background for
296   // the search box. This is needed because this view paints to a layer and
297   // won't propagate paints upward.
298   if (parent())
299     parent()->SchedulePaint();
300 }
301
302 void AppListMainView::ActivateApp(AppListItem* item, int event_flags) {
303   // TODO(jennyz): Activate the folder via AppListModel notification.
304   if (item->GetItemType() == AppListFolderItem::kItemType)
305     contents_view_->ShowFolderContent(static_cast<AppListFolderItem*>(item));
306   else
307     item->Activate(event_flags);
308 }
309
310 void AppListMainView::GetShortcutPathForApp(
311     const std::string& app_id,
312     const base::Callback<void(const base::FilePath&)>& callback) {
313   delegate_->GetShortcutPathForApp(app_id, callback);
314 }
315
316 void AppListMainView::CancelDragInActiveFolder() {
317   contents_view_->apps_container_view()
318       ->app_list_folder_view()
319       ->items_grid_view()
320       ->EndDrag(true);
321 }
322
323 void AppListMainView::QueryChanged(SearchBoxView* sender) {
324   base::string16 query;
325   base::TrimWhitespace(model_->search_box()->text(), base::TRIM_ALL, &query);
326   bool should_show_search = !query.empty();
327   contents_view_->ShowSearchResults(should_show_search);
328   UpdateSearchBoxVisibility();
329
330   delegate_->StartSearch();
331 }
332
333 void AppListMainView::OnResultInstalled(SearchResult* result) {
334   // Clears the search to show the apps grid. The last installed app
335   // should be highlighted and made visible already.
336   search_box_view_->ClearSearch();
337 }
338
339 void AppListMainView::OnResultUninstalled(SearchResult* result) {
340   // Resubmit the query via a posted task so that all observers for the
341   // uninstall notification are notified.
342   base::MessageLoop::current()->PostTask(
343       FROM_HERE,
344       base::Bind(&AppListMainView::QueryChanged,
345                  weak_ptr_factory_.GetWeakPtr(),
346                  search_box_view_));
347 }
348
349 }  // namespace app_list