Home | History | Annotate | Download | only in views
      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:
     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 
     60   virtual ~IconLoader() {
     61     item_->RemoveObserver(this);
     62   }
     63 
     64  private:
     65   // AppListItemObserver overrides:
     66   virtual void ItemIconChanged() OVERRIDE {
     67     owner_->OnItemIconLoaded(this);
     68     // Note that IconLoader is released here.
     69   }
     70   virtual void ItemNameChanged() OVERRIDE {}
     71   virtual void ItemHighlightedChanged() OVERRIDE {}
     72   virtual void ItemIsInstallingChanged() OVERRIDE {}
     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 
     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 
    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 
    129 AppListMainView::~AppListMainView() {
    130   pending_icon_loaders_.clear();
    131 }
    132 
    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 
    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 
    156 void AppListMainView::Close() {
    157   icon_loading_wait_timer_.Stop();
    158   contents_view_->CancelDrag();
    159 }
    160 
    161 void AppListMainView::Prerender() {
    162   contents_view_->Prerender();
    163 }
    164 
    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 
    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 
    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 
    195 void AppListMainView::SetDragAndDropHostOfCurrentAppList(
    196     ApplicationDragAndDropHost* drag_and_drop_host) {
    197   contents_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
    198 }
    199 
    200 bool AppListMainView::ShouldCenterWindow() const {
    201   return delegate_->ShouldCenterWindow();
    202 }
    203 
    204 PaginationModel* AppListMainView::GetAppsPaginationModel() {
    205   return contents_view_->apps_container_view()
    206       ->apps_grid_view()
    207       ->pagination_model();
    208 }
    209 
    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 
    240 void AppListMainView::OnIconLoadingWaitTimer() {
    241   GetWidget()->Show();
    242 }
    243 
    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 
    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 
    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 
    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 
    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 
    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 
    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