• 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_item_view.h"
23 #include "ui/app_list/views/apps_container_view.h"
24 #include "ui/app_list/views/apps_grid_view.h"
25 #include "ui/app_list/views/contents_switcher_view.h"
26 #include "ui/app_list/views/contents_view.h"
27 #include "ui/app_list/views/search_box_view.h"
28 #include "ui/views/controls/textfield/textfield.h"
29 #include "ui/views/layout/box_layout.h"
30 #include "ui/views/widget/widget.h"
31 
32 namespace app_list {
33 
34 namespace {
35 
36 // Inner padding space in pixels of bubble contents.
37 const int kInnerPadding = 1;
38 
39 // The maximum allowed time to wait for icon loading in milliseconds.
40 const int kMaxIconLoadingWaitTimeInMs = 50;
41 
42 }  // namespace
43 
44 ////////////////////////////////////////////////////////////////////////////////
45 // AppListMainView::IconLoader
46 
47 class AppListMainView::IconLoader : public AppListItemObserver {
48  public:
IconLoader(AppListMainView * owner,AppListItem * item,float scale)49   IconLoader(AppListMainView* owner,
50              AppListItem* item,
51              float scale)
52       : owner_(owner),
53         item_(item) {
54     item_->AddObserver(this);
55 
56     // Triggers icon loading for given |scale_factor|.
57     item_->icon().GetRepresentation(scale);
58   }
59 
~IconLoader()60   virtual ~IconLoader() {
61     item_->RemoveObserver(this);
62   }
63 
64  private:
65   // AppListItemObserver overrides:
ItemIconChanged()66   virtual void ItemIconChanged() OVERRIDE {
67     owner_->OnItemIconLoaded(this);
68     // Note that IconLoader is released here.
69   }
ItemNameChanged()70   virtual void ItemNameChanged() OVERRIDE {}
ItemHighlightedChanged()71   virtual void ItemHighlightedChanged() OVERRIDE {}
ItemIsInstallingChanged()72   virtual void ItemIsInstallingChanged() OVERRIDE {}
ItemPercentDownloadedChanged()73   virtual void ItemPercentDownloadedChanged() OVERRIDE {}
74 
75   AppListMainView* owner_;
76   AppListItem* item_;
77 
78   DISALLOW_COPY_AND_ASSIGN(IconLoader);
79 };
80 
81 ////////////////////////////////////////////////////////////////////////////////
82 // AppListMainView:
83 
AppListMainView(AppListViewDelegate * delegate,int initial_apps_page,gfx::NativeView parent)84 AppListMainView::AppListMainView(AppListViewDelegate* delegate,
85                                  int initial_apps_page,
86                                  gfx::NativeView parent)
87     : delegate_(delegate),
88       model_(delegate->GetModel()),
89       search_box_view_(NULL),
90       contents_view_(NULL),
91       contents_switcher_view_(NULL),
92       weak_ptr_factory_(this) {
93   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
94                                         kInnerPadding,
95                                         kInnerPadding,
96                                         kInnerPadding));
97 
98   search_box_view_ = new SearchBoxView(this, delegate);
99   AddChildView(search_box_view_);
100   AddContentsViews();
101 
102   // Switch the apps grid view to the specified page.
103   app_list::PaginationModel* pagination_model = GetAppsPaginationModel();
104   if (pagination_model->is_valid_page(initial_apps_page))
105     pagination_model->SelectPage(initial_apps_page, false);
106 
107   // Starts icon loading early.
108   PreloadIcons(parent);
109 }
110 
AddContentsViews()111 void AppListMainView::AddContentsViews() {
112   contents_view_ = new ContentsView(this);
113   if (app_list::switches::IsExperimentalAppListEnabled()) {
114     contents_switcher_view_ = new ContentsSwitcherView(contents_view_);
115     contents_view_->set_contents_switcher_view(contents_switcher_view_);
116   }
117   contents_view_->InitNamedPages(model_, delegate_);
118   AddChildView(contents_view_);
119   if (contents_switcher_view_)
120     AddChildView(contents_switcher_view_);
121 
122   search_box_view_->set_contents_view(contents_view_);
123 
124   contents_view_->SetPaintToLayer(true);
125   contents_view_->SetFillsBoundsOpaquely(false);
126   contents_view_->layer()->SetMasksToBounds(true);
127 }
128 
~AppListMainView()129 AppListMainView::~AppListMainView() {
130   pending_icon_loaders_.clear();
131 }
132 
ShowAppListWhenReady()133 void AppListMainView::ShowAppListWhenReady() {
134   if (pending_icon_loaders_.empty()) {
135     icon_loading_wait_timer_.Stop();
136     GetWidget()->Show();
137     return;
138   }
139 
140   if (icon_loading_wait_timer_.IsRunning())
141     return;
142 
143   icon_loading_wait_timer_.Start(
144       FROM_HERE,
145       base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs),
146       this, &AppListMainView::OnIconLoadingWaitTimer);
147 }
148 
ResetForShow()149 void AppListMainView::ResetForShow() {
150   contents_view_->apps_container_view()->ResetForShowApps();
151   // We clear the search when hiding so when app list appears it is not showing
152   // search results.
153   search_box_view_->ClearSearch();
154 }
155 
Close()156 void AppListMainView::Close() {
157   icon_loading_wait_timer_.Stop();
158   contents_view_->CancelDrag();
159 }
160 
Prerender()161 void AppListMainView::Prerender() {
162   contents_view_->Prerender();
163 }
164 
ModelChanged()165 void AppListMainView::ModelChanged() {
166   pending_icon_loaders_.clear();
167   model_ = delegate_->GetModel();
168   search_box_view_->ModelChanged();
169   delete contents_view_;
170   contents_view_ = NULL;
171   if (contents_switcher_view_) {
172     delete contents_switcher_view_;
173     contents_switcher_view_ = NULL;
174   }
175   AddContentsViews();
176   Layout();
177 }
178 
UpdateSearchBoxVisibility()179 void AppListMainView::UpdateSearchBoxVisibility() {
180   bool visible =
181       !contents_view_->IsNamedPageActive(ContentsView::NAMED_PAGE_START) ||
182       contents_view_->IsShowingSearchResults();
183   search_box_view_->SetVisible(visible);
184   if (visible && GetWidget()->IsVisible())
185     search_box_view_->search_box()->RequestFocus();
186 }
187 
OnStartPageSearchTextfieldChanged(const base::string16 & new_contents)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();
193 }
194 
SetDragAndDropHostOfCurrentAppList(ApplicationDragAndDropHost * drag_and_drop_host)195 void AppListMainView::SetDragAndDropHostOfCurrentAppList(
196     ApplicationDragAndDropHost* drag_and_drop_host) {
197   contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
198 }
199 
ShouldCenterWindow() const200 bool AppListMainView::ShouldCenterWindow() const {
201   return delegate_->ShouldCenterWindow();
202 }
203 
GetAppsPaginationModel()204 PaginationModel* AppListMainView::GetAppsPaginationModel() {
205   return contents_view_->apps_container_view()
206       ->apps_grid_view()
207       ->pagination_model();
208 }
209 
PreloadIcons(gfx::NativeView parent)210 void AppListMainView::PreloadIcons(gfx::NativeView parent) {
211   float scale_factor = 1.0f;
212   if (parent)
213     scale_factor = ui::GetScaleFactorForNativeView(parent);
214 
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());
219 
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();
224 
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);
229 
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))
234       continue;
235 
236     pending_icon_loaders_.push_back(new IconLoader(this, item, scale_factor));
237   }
238 }
239 
OnIconLoadingWaitTimer()240 void AppListMainView::OnIconLoadingWaitTimer() {
241   GetWidget()->Show();
242 }
243 
OnItemIconLoaded(IconLoader * loader)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);
249 
250   if (pending_icon_loaders_.empty() && icon_loading_wait_timer_.IsRunning()) {
251     icon_loading_wait_timer_.Stop();
252     GetWidget()->Show();
253   }
254 }
255 
ChildVisibilityChanged(views::View * child)256 void AppListMainView::ChildVisibilityChanged(views::View* child) {
257   // Repaint the AppListView's background which will repaint the background for
258   // the search box.
259   if (child == search_box_view_ && parent())
260     parent()->SchedulePaint();
261 }
262 
ActivateApp(AppListItem * item,int event_flags)263 void AppListMainView::ActivateApp(AppListItem* item, int event_flags) {
264   // TODO(jennyz): Activate the folder via AppListModel notification.
265   if (item->GetItemType() == AppListFolderItem::kItemType)
266     contents_view_->ShowFolderContent(static_cast<AppListFolderItem*>(item));
267   else
268     item->Activate(event_flags);
269 }
270 
GetShortcutPathForApp(const std::string & app_id,const base::Callback<void (const base::FilePath &)> & callback)271 void AppListMainView::GetShortcutPathForApp(
272     const std::string& app_id,
273     const base::Callback<void(const base::FilePath&)>& callback) {
274   delegate_->GetShortcutPathForApp(app_id, callback);
275 }
276 
QueryChanged(SearchBoxView * sender)277 void AppListMainView::QueryChanged(SearchBoxView* sender) {
278   base::string16 query;
279   base::TrimWhitespace(model_->search_box()->text(), base::TRIM_ALL, &query);
280   bool should_show_search = !query.empty();
281   contents_view_->ShowSearchResults(should_show_search);
282   UpdateSearchBoxVisibility();
283 
284   if (should_show_search)
285     delegate_->StartSearch();
286   else
287     delegate_->StopSearch();
288 }
289 
OnResultInstalled(SearchResult * result)290 void AppListMainView::OnResultInstalled(SearchResult* result) {
291   // Clears the search to show the apps grid. The last installed app
292   // should be highlighted and made visible already.
293   search_box_view_->ClearSearch();
294 }
295 
OnResultUninstalled(SearchResult * result)296 void AppListMainView::OnResultUninstalled(SearchResult* result) {
297   // Resubmit the query via a posted task so that all observers for the
298   // uninstall notification are notified.
299   base::MessageLoop::current()->PostTask(
300       FROM_HERE,
301       base::Bind(&AppListMainView::QueryChanged,
302                  weak_ptr_factory_.GetWeakPtr(),
303                  search_box_view_));
304 }
305 
306 }  // namespace app_list
307