• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
SearchBoxContainerView(AppListMainView * host,SearchBoxView * search_box)51   SearchBoxContainerView(AppListMainView* host, SearchBoxView* search_box)
52       : host_(host), search_box_(search_box) {
53     SetLayoutManager(new views::FillLayout());
54     AddChildView(search_box);
55   }
~SearchBoxContainerView()56   virtual ~SearchBoxContainerView() {}
57 
58  private:
59   // Overridden from views::View:
ChildVisibilityChanged(views::View * child)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:
IconLoader(AppListMainView * owner,AppListItem * item,float scale)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 
~IconLoader()89   virtual ~IconLoader() {
90     item_->RemoveObserver(this);
91   }
92 
93  private:
94   // AppListItemObserver overrides:
ItemIconChanged()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 
AppListMainView(AppListViewDelegate * delegate,int initial_apps_page,gfx::NativeView parent)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 
AddContentsViews()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 
~AppListMainView()163 AppListMainView::~AppListMainView() {
164   pending_icon_loaders_.clear();
165 }
166 
ShowAppListWhenReady()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 
ResetForShow()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 
Close()194 void AppListMainView::Close() {
195   icon_loading_wait_timer_.Stop();
196   contents_view_->CancelDrag();
197 }
198 
Prerender()199 void AppListMainView::Prerender() {
200   contents_view_->Prerender();
201 }
202 
ModelChanged()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 
UpdateSearchBoxVisibility()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 
OnStartPageSearchTextfieldChanged(const base::string16 & new_contents)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 
SetDragAndDropHostOfCurrentAppList(ApplicationDragAndDropHost * drag_and_drop_host)233 void AppListMainView::SetDragAndDropHostOfCurrentAppList(
234     ApplicationDragAndDropHost* drag_and_drop_host) {
235   contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
236 }
237 
ShouldCenterWindow() const238 bool AppListMainView::ShouldCenterWindow() const {
239   return delegate_->ShouldCenterWindow();
240 }
241 
GetAppsPaginationModel()242 PaginationModel* AppListMainView::GetAppsPaginationModel() {
243   return contents_view_->apps_container_view()
244       ->apps_grid_view()
245       ->pagination_model();
246 }
247 
PreloadIcons(gfx::NativeView parent)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 
OnIconLoadingWaitTimer()278 void AppListMainView::OnIconLoadingWaitTimer() {
279   GetWidget()->Show();
280 }
281 
OnItemIconLoaded(IconLoader * loader)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 
NotifySearchBoxVisibilityChanged()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 
ActivateApp(AppListItem * item,int event_flags)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 
GetShortcutPathForApp(const std::string & app_id,const base::Callback<void (const base::FilePath &)> & callback)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 
CancelDragInActiveFolder()316 void AppListMainView::CancelDragInActiveFolder() {
317   contents_view_->apps_container_view()
318       ->app_list_folder_view()
319       ->items_grid_view()
320       ->EndDrag(true);
321 }
322 
QueryChanged(SearchBoxView * sender)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 
OnResultInstalled(SearchResult * result)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 
OnResultUninstalled(SearchResult * result)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
350