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