| Index: ui/app_list/app_list_view.cc
|
| diff --git a/ui/app_list/app_list_view.cc b/ui/app_list/app_list_view.cc
|
| index c4e4e3aa3b41ef2b0b8c2d8441adc089fc6d1254..2e07bcd3036f08b0170e961aa71b03de9cd87fac 100644
|
| --- a/ui/app_list/app_list_view.cc
|
| +++ b/ui/app_list/app_list_view.cc
|
| @@ -4,9 +4,12 @@
|
|
|
| #include "ui/app_list/app_list_view.h"
|
|
|
| +#include <algorithm>
|
| +
|
| #include "base/string_util.h"
|
| #include "ui/app_list/app_list_background.h"
|
| #include "ui/app_list/app_list_constants.h"
|
| +#include "ui/app_list/app_list_item_model.h"
|
| #include "ui/app_list/app_list_item_view.h"
|
| #include "ui/app_list/app_list_model.h"
|
| #include "ui/app_list/app_list_view_delegate.h"
|
| @@ -33,20 +36,62 @@ const int kInnerPadding = 1;
|
| // The distance between the arrow tip and edge of the anchor view.
|
| const int kArrowOffset = 10;
|
|
|
| +// The maximum allowed time to wait for icon loading in milliseconds.
|
| +const int kMaxIconLoadingWaitTimeInMs = 50;
|
| +
|
| } // namespace
|
|
|
| ////////////////////////////////////////////////////////////////////////////////
|
| +// AppListView::IconLoader
|
| +
|
| +class AppListView::IconLoader : public AppListItemModelObserver {
|
| + public:
|
| + IconLoader(AppListView* owner,
|
| + AppListItemModel* item,
|
| + ui::ScaleFactor scale_factor)
|
| + : owner_(owner),
|
| + item_(item) {
|
| + item_->AddObserver(this);
|
| +
|
| + // Triggers icon loading for given |scale_factor|.
|
| + item_->icon().GetRepresentation(scale_factor);
|
| + }
|
| +
|
| + virtual ~IconLoader() {
|
| + item_->RemoveObserver(this);
|
| + }
|
| +
|
| + private:
|
| + // AppListItemModelObserver overrides:
|
| + virtual void ItemIconChanged() OVERRIDE {
|
| + owner_->OnItemIconLoaded(this);
|
| + // Note that IconLoader is released here.
|
| + }
|
| + virtual void ItemTitleChanged() OVERRIDE {}
|
| + virtual void ItemHighlightedChanged() OVERRIDE {}
|
| +
|
| + AppListView* owner_;
|
| + AppListItemModel* item_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(IconLoader);
|
| +};
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| // AppListView:
|
|
|
| AppListView::AppListView(AppListViewDelegate* delegate)
|
| - : delegate_(delegate),
|
| + : model_(new AppListModel),
|
| + delegate_(delegate),
|
| search_box_view_(NULL),
|
| contents_view_(NULL) {
|
| + if (delegate_)
|
| + delegate_->SetModel(model_.get());
|
| }
|
|
|
| AppListView::~AppListView() {
|
| - // Deletes all child views while the models are still valid.
|
| + // Models are going away, ensure their references are cleared.
|
| RemoveAllChildViews(true);
|
| + pending_icon_loaders_.clear();
|
| }
|
|
|
| void AppListView::InitAsBubble(
|
| @@ -55,6 +100,9 @@ void AppListView::InitAsBubble(
|
| views::View* anchor,
|
| const gfx::Point& anchor_point,
|
| views::BubbleBorder::ArrowLocation arrow_location) {
|
| + // Starts icon loading early.
|
| + PreloadIcons(pagination_model, anchor);
|
| +
|
| SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical,
|
| kInnerPadding,
|
| kInnerPadding,
|
| @@ -96,7 +144,8 @@ void AppListView::InitAsBubble(
|
| search_box_view_));
|
| #endif
|
|
|
| - CreateModel();
|
| + search_box_view_->SetModel(model_->search_box());
|
| + contents_view_->SetModel(model_.get());
|
| }
|
|
|
| void AppListView::SetBubbleArrowLocation(
|
| @@ -111,7 +160,25 @@ void AppListView::SetAnchorPoint(const gfx::Point& anchor_point) {
|
| SizeToContents(); // Repositions view relative to the anchor.
|
| }
|
|
|
| +void AppListView::ShowWhenReady() {
|
| + if (pending_icon_loaders_.empty()) {
|
| + icon_loading_wait_timer_.Stop();
|
| + GetWidget()->Show();
|
| + return;
|
| + }
|
| +
|
| + if (icon_loading_wait_timer_.IsRunning())
|
| + return;
|
| +
|
| + icon_loading_wait_timer_.Start(
|
| + FROM_HERE,
|
| + base::TimeDelta::FromMilliseconds(kMaxIconLoadingWaitTimeInMs),
|
| + this, &AppListView::OnIconLoadingWaitTimer);
|
| +}
|
| +
|
| void AppListView::Close() {
|
| + icon_loading_wait_timer_.Stop();
|
| +
|
| if (delegate_.get())
|
| delegate_->Close();
|
| else
|
| @@ -122,16 +189,47 @@ void AppListView::UpdateBounds() {
|
| SizeToContents();
|
| }
|
|
|
| -void AppListView::CreateModel() {
|
| - if (delegate_.get()) {
|
| - // Creates a new model and update all references before releasing old one.
|
| - scoped_ptr<AppListModel> new_model(new AppListModel);
|
| +void AppListView::PreloadIcons(PaginationModel* pagination_model,
|
| + views::View* anchor) {
|
| + ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_100P;
|
| + if (anchor && anchor->GetWidget()) {
|
| + scale_factor = ui::GetScaleFactorForNativeView(
|
| + anchor->GetWidget()->GetNativeView());
|
| + }
|
| +
|
| + // |pagination_model| could have -1 as the initial selected page and
|
| + // assumes first page (i.e. index 0) will be used in this case.
|
| + const int selected_page = std::max(0, pagination_model->selected_page());
|
| +
|
| + const int tiles_per_page = kPreferredCols * kPreferredRows;
|
| + const int start_model_index = selected_page * tiles_per_page;
|
| + const int end_model_index = std::min(
|
| + static_cast<int>(model_->apps()->item_count()),
|
| + start_model_index + tiles_per_page);
|
| +
|
| + pending_icon_loaders_.clear();
|
| + for (int i = start_model_index; i < end_model_index; ++i) {
|
| + AppListItemModel* item = model_->apps()->GetItemAt(i);
|
| + if (item->icon().HasRepresentation(scale_factor))
|
| + continue;
|
| +
|
| + pending_icon_loaders_.push_back(new IconLoader(this, item, scale_factor));
|
| + }
|
| +}
|
| +
|
| +void AppListView::OnIconLoadingWaitTimer() {
|
| + GetWidget()->Show();
|
| +}
|
|
|
| - delegate_->SetModel(new_model.get());
|
| - search_box_view_->SetModel(new_model->search_box());
|
| - contents_view_->SetModel(new_model.get());
|
| +void AppListView::OnItemIconLoaded(IconLoader* loader) {
|
| + ScopedVector<IconLoader>::iterator it = std::find(
|
| + pending_icon_loaders_.begin(), pending_icon_loaders_.end(), loader);
|
| + DCHECK(it != pending_icon_loaders_.end());
|
| + pending_icon_loaders_.erase(it);
|
|
|
| - model_.reset(new_model.release());
|
| + if (pending_icon_loaders_.empty() && icon_loading_wait_timer_.IsRunning()) {
|
| + icon_loading_wait_timer_.Stop();
|
| + GetWidget()->Show();
|
| }
|
| }
|
|
|
|
|