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_model.h" 17 #include "ui/app_list/app_list_model.h" 18 #include "ui/app_list/app_list_view_delegate.h" 19 #include "ui/app_list/pagination_model.h" 20 #include "ui/app_list/search_box_model.h" 21 #include "ui/app_list/views/app_list_item_view.h" 22 #include "ui/app_list/views/contents_view.h" 23 #include "ui/app_list/views/search_box_view.h" 24 #include "ui/views/controls/textfield/textfield.h" 25 #include "ui/views/layout/box_layout.h" 26 #include "ui/views/widget/widget.h" 27 28 namespace app_list { 29 30 namespace { 31 32 // Inner padding space in pixels of bubble contents. 33 const int kInnerPadding = 1; 34 35 // The maximum allowed time to wait for icon loading in milliseconds. 36 const int kMaxIconLoadingWaitTimeInMs = 50; 37 38 } // namespace 39 40 //////////////////////////////////////////////////////////////////////////////// 41 // AppListMainView::IconLoader 42 43 class AppListMainView::IconLoader : public AppListItemModelObserver { 44 public: 45 IconLoader(AppListMainView* owner, 46 AppListItemModel* item, 47 float scale) 48 : owner_(owner), 49 item_(item) { 50 item_->AddObserver(this); 51 52 // Triggers icon loading for given |scale_factor|. 53 item_->icon().GetRepresentation(scale); 54 } 55 56 virtual ~IconLoader() { 57 item_->RemoveObserver(this); 58 } 59 60 private: 61 // AppListItemModelObserver overrides: 62 virtual void ItemIconChanged() OVERRIDE { 63 owner_->OnItemIconLoaded(this); 64 // Note that IconLoader is released here. 65 } 66 virtual void ItemTitleChanged() OVERRIDE {} 67 virtual void ItemHighlightedChanged() OVERRIDE {} 68 virtual void ItemIsInstallingChanged() OVERRIDE {} 69 virtual void ItemPercentDownloadedChanged() OVERRIDE {} 70 71 AppListMainView* owner_; 72 AppListItemModel* item_; 73 74 DISALLOW_COPY_AND_ASSIGN(IconLoader); 75 }; 76 77 //////////////////////////////////////////////////////////////////////////////// 78 // AppListMainView: 79 80 AppListMainView::AppListMainView(AppListViewDelegate* delegate, 81 PaginationModel* pagination_model, 82 gfx::NativeView parent) 83 : delegate_(delegate), 84 pagination_model_(pagination_model), 85 model_(delegate->GetModel()), 86 search_box_view_(NULL), 87 contents_view_(NULL), 88 weak_ptr_factory_(this) { 89 // Starts icon loading early. 90 PreloadIcons(parent); 91 92 SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 93 kInnerPadding, 94 kInnerPadding, 95 kInnerPadding)); 96 97 search_box_view_ = new SearchBoxView(this, delegate); 98 AddChildView(search_box_view_); 99 AddContentsView(); 100 } 101 102 void AppListMainView::AddContentsView() { 103 contents_view_ = 104 new ContentsView(this, 105 pagination_model_, 106 model_, 107 delegate_ ? delegate_->GetStartPageContents() : NULL); 108 AddChildView(contents_view_); 109 110 search_box_view_->set_contents_view(contents_view_); 111 112 #if defined(USE_AURA) 113 contents_view_->SetPaintToLayer(true); 114 contents_view_->SetFillsBoundsOpaquely(false); 115 contents_view_->layer()->SetMasksToBounds(true); 116 #endif 117 } 118 119 AppListMainView::~AppListMainView() { 120 pending_icon_loaders_.clear(); 121 } 122 123 void AppListMainView::ShowAppListWhenReady() { 124 if (pending_icon_loaders_.empty()) { 125 icon_loading_wait_timer_.Stop(); 126 GetWidget()->Show(); 127 return; 128 } 129 130 if (icon_loading_wait_timer_.IsRunning()) 131 return; 132 133 icon_loading_wait_timer_.Start( 134 FROM_HERE, 135 base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs), 136 this, &AppListMainView::OnIconLoadingWaitTimer); 137 } 138 139 void AppListMainView::Close() { 140 icon_loading_wait_timer_.Stop(); 141 contents_view_->CancelDrag(); 142 } 143 144 void AppListMainView::Prerender() { 145 contents_view_->Prerender(); 146 } 147 148 void AppListMainView::ModelChanged() { 149 pending_icon_loaders_.clear(); 150 model_ = delegate_->GetModel(); 151 search_box_view_->ModelChanged(); 152 delete contents_view_; 153 contents_view_ = NULL; 154 pagination_model_->SelectPage(0, false /* animate */); 155 AddContentsView(); 156 Layout(); 157 } 158 159 void AppListMainView::SetDragAndDropHostOfCurrentAppList( 160 ApplicationDragAndDropHost* drag_and_drop_host) { 161 contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host); 162 } 163 164 void AppListMainView::PreloadIcons(gfx::NativeView parent) { 165 ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_100P; 166 if (parent) 167 scale_factor = ui::GetScaleFactorForNativeView(parent); 168 169 float scale = ui::GetImageScale(scale_factor); 170 // |pagination_model| could have -1 as the initial selected page and 171 // assumes first page (i.e. index 0) will be used in this case. 172 const int selected_page = std::max(0, pagination_model_->selected_page()); 173 174 const int tiles_per_page = kPreferredCols * kPreferredRows; 175 const int start_model_index = selected_page * tiles_per_page; 176 const int end_model_index = std::min( 177 static_cast<int>(model_->item_list()->item_count()), 178 start_model_index + tiles_per_page); 179 180 pending_icon_loaders_.clear(); 181 for (int i = start_model_index; i < end_model_index; ++i) { 182 AppListItemModel* item = model_->item_list()->item_at(i); 183 if (item->icon().HasRepresentation(scale)) 184 continue; 185 186 pending_icon_loaders_.push_back(new IconLoader(this, item, scale)); 187 } 188 } 189 190 void AppListMainView::OnIconLoadingWaitTimer() { 191 GetWidget()->Show(); 192 } 193 194 void AppListMainView::OnItemIconLoaded(IconLoader* loader) { 195 ScopedVector<IconLoader>::iterator it = std::find( 196 pending_icon_loaders_.begin(), pending_icon_loaders_.end(), loader); 197 DCHECK(it != pending_icon_loaders_.end()); 198 pending_icon_loaders_.erase(it); 199 200 if (pending_icon_loaders_.empty() && icon_loading_wait_timer_.IsRunning()) { 201 icon_loading_wait_timer_.Stop(); 202 GetWidget()->Show(); 203 } 204 } 205 206 void AppListMainView::ActivateApp(AppListItemModel* item, int event_flags) { 207 // TODO(jennyz): Activate the folder via AppListModel notification. 208 if (item->GetAppType() == AppListFolderItem::kAppType) 209 contents_view_->ShowFolderContent(static_cast<AppListFolderItem*>(item)); 210 else 211 item->Activate(event_flags); 212 } 213 214 void AppListMainView::GetShortcutPathForApp( 215 const std::string& app_id, 216 const base::Callback<void(const base::FilePath&)>& callback) { 217 delegate_->GetShortcutPathForApp(app_id, callback); 218 } 219 220 void AppListMainView::QueryChanged(SearchBoxView* sender) { 221 base::string16 query; 222 TrimWhitespace(model_->search_box()->text(), TRIM_ALL, &query); 223 bool should_show_search = !query.empty(); 224 contents_view_->ShowSearchResults(should_show_search); 225 226 if (should_show_search) 227 delegate_->StartSearch(); 228 else 229 delegate_->StopSearch(); 230 } 231 232 void AppListMainView::OpenResult(SearchResult* result, int event_flags) { 233 delegate_->OpenSearchResult(result, event_flags); 234 } 235 236 void AppListMainView::InvokeResultAction(SearchResult* result, 237 int action_index, 238 int event_flags) { 239 delegate_->InvokeSearchResultAction(result, action_index, event_flags); 240 } 241 242 void AppListMainView::OnResultInstalled(SearchResult* result) { 243 // Clears the search to show the apps grid. The last installed app 244 // should be highlighted and made visible already. 245 search_box_view_->ClearSearch(); 246 } 247 248 void AppListMainView::OnResultUninstalled(SearchResult* result) { 249 // Resubmit the query via a posted task so that all observers for the 250 // uninstall notification are notified. 251 base::MessageLoop::current()->PostTask( 252 FROM_HERE, 253 base::Bind(&AppListMainView::QueryChanged, 254 weak_ptr_factory_.GetWeakPtr(), 255 search_box_view_)); 256 } 257 258 } // namespace app_list 259