Index: ui/app_list/app_list_model.cc |
diff --git a/ui/app_list/app_list_model.cc b/ui/app_list/app_list_model.cc |
index 246465685685f97cfd3878f592a87f4418d7f4f0..d039058de1578d93429b4dbdf148df8eabb601ba 100644 |
--- a/ui/app_list/app_list_model.cc |
+++ b/ui/app_list/app_list_model.cc |
@@ -4,10 +4,12 @@ |
#include "ui/app_list/app_list_model.h" |
+#include "ui/app_list/app_list_constants.h" |
#include "ui/app_list/app_list_item_model.h" |
#include "ui/app_list/app_list_model_observer.h" |
#include "ui/app_list/search_box_model.h" |
#include "ui/app_list/search_result.h" |
+#include "ui/base/models/list_model_observer.h" |
namespace app_list { |
@@ -15,15 +17,56 @@ AppListModel::User::User() : active(false) {} |
AppListModel::User::~User() {} |
+class AppListModel::ItemPage : public ui::ListModelObserver { |
+ public: |
+ ItemPage(AppListModel* model, size_t page_idx) |
+ : model_(model), |
+ page_idx_(page_idx) { |
+ app_items_.AddObserver(this); |
+ } |
+ virtual ~ItemPage() { |
+ app_items_.RemoveObserver(this); |
+ } |
+ virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE { |
+ FOR_EACH_OBSERVER(AppListModelObserver, |
+ model_->observers_, |
+ OnListItemsAdded(page_idx_, start, count)); |
+ } |
+ virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE { |
+ FOR_EACH_OBSERVER(AppListModelObserver, |
+ model_->observers_, |
+ OnListItemsRemoved(page_idx_, start, count)); |
+ } |
+ virtual void ListItemMoved(size_t index, size_t target_index) OVERRIDE { |
+ AppListItemModel* item = app_items_.GetItemAt(target_index); |
+ model_->SetItemPosition(item, page_idx_, target_index); |
+ FOR_EACH_OBSERVER(AppListModelObserver, |
+ model_->observers_, |
+ OnListItemMoved(page_idx_, index, target_index)); |
+ } |
+ virtual void ListItemsChanged(size_t start, size_t count) OVERRIDE { |
+ NOTREACHED(); |
+ } |
+ |
+ AppItems* app_items() { return &app_items_; } |
+ |
+ private: |
+ AppListModel* model_; |
+ size_t page_idx_; |
+ AppItems app_items_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ItemPage); |
+}; |
+ |
AppListModel::AppListModel() |
- : apps_(new Apps), |
- search_box_(new SearchBoxModel), |
+ : search_box_(new SearchBoxModel), |
results_(new SearchResults), |
signed_in_(false), |
status_(STATUS_NORMAL) { |
} |
AppListModel::~AppListModel() { |
+ item_pages_.clear(); |
} |
void AppListModel::AddObserver(AppListModelObserver* observer) { |
@@ -62,33 +105,196 @@ void AppListModel::SetSignedIn(bool signed_in) { |
} |
AppListItemModel* AppListModel::FindItem(const std::string& id) { |
- for (size_t i = 0; i < apps_->item_count(); ++i) { |
- AppListItemModel* item = apps_->GetItemAt(i); |
- if (item->id() == id) |
- return item; |
+ for (size_t page = 0; page < item_pages_.size(); ++page) { |
+ AppItems* apps = app_items(page); |
+ for (size_t i = 0; i < apps->item_count(); ++i) { |
+ AppListItemModel* item = apps->GetItemAt(i); |
+ if (item->id() == id) |
+ return item; |
+ } |
} |
return NULL; |
} |
-void AppListModel::AddItem(AppListItemModel* item) { |
+ |
+AppListItemModel* AppListModel::GetItemAt(size_t page_idx, size_t item_idx) { |
+ return app_items(page_idx)->GetItemAt(item_idx); |
+} |
+ |
+size_t AppListModel::AddItem(AppListItemModel* item) { |
+ CHECK(!FindItem(item->id())); |
+ if (item_pages_.empty()) |
+ return AddItemToPageAtIndex(item, 0, 0); |
+ |
+ // Currently items are kept sorted in the model such that for any item A |
+ // on page 1 and item B on page 2, A.GetSortOrder() < B.GetSortOrder(). This |
+ // also applies to any item A on a page that preceeds B on the same page. |
std::string sort_order = item->GetSortOrder(); |
- // Note: ui::ListModel is not a sorted list. |
- size_t index = 0; |
- for (; index < apps_->item_count(); ++index) { |
- if (sort_order < apps_->GetItemAt(index)->GetSortOrder()) |
+ |
+ // First find the page that |item| belongs in. |
+ size_t page_idx = 0; |
+ for (; page_idx < item_pages_.size() - 1; ++page_idx) { |
+ if (sort_order < app_items(page_idx + 1)->GetItemAt(0)->GetSortOrder()) |
break; |
} |
- apps_->AddAt(index, item); |
+ // Items are kept sorted in a page by convention, but are not logically |
+ // gauranteed to be sorted (e.g. if an item's sort order were to change) |
+ // so do a linear search instead of using std::find (also the number of items |
+ // per page is small and a linear search is simpler). |
+ AppItems* apps = app_items(page_idx); |
+ size_t item_idx = 0; |
+ for (; item_idx < apps->item_count(); ++item_idx) { |
+ if (sort_order < apps->GetItemAt(item_idx)->GetSortOrder()) |
+ break; |
+ } |
+ return AddItemToPageAtIndex(item, page_idx, item_idx); |
+} |
+ |
+size_t AppListModel::AddItemToPage(AppListItemModel* item, size_t page_idx) { |
+ CHECK(!FindItem(item->id())); |
+ if (page_idx == item_pages_.size()) { |
+ // OK to add an item to [number of app pages] + 1. |
+ return AddItemToPageAtIndex(item, page_idx, 0); |
+ } |
+ std::string sort_order = item->GetSortOrder(); |
+ AppItems* apps = app_items(page_idx); |
+ size_t item_idx = 0; |
+ for (; item_idx < apps->item_count(); ++item_idx) { |
xiyuan
2013/10/18 23:04:08
Can we make this loop of insertion an item part of
stevenjb
2013/10/19 01:04:43
That sounds reasonable. I kind of wanted to keep t
|
+ if (sort_order < apps->GetItemAt(item_idx)->GetSortOrder()) |
+ break; |
+ } |
+ return AddItemToPageAtIndex(item, page_idx, item_idx); |
} |
void AppListModel::DeleteItem(const std::string& id) { |
- for (size_t i = 0; i < apps_->item_count(); ++i) { |
- AppListItemModel* item = apps_->GetItemAt(i); |
- if (item->id() == id) { |
- apps_->DeleteAt(i); |
- return; |
+ for (size_t p = 0; p < item_pages_.size(); ++p) { |
+ AppItems* apps = app_items(p); |
+ for (size_t i = 0; i < apps->item_count(); ++i) { |
+ AppListItemModel* item = apps->GetItemAt(i); |
+ if (item->id() == id) { |
+ apps->DeleteAt(i); |
+ if (apps->item_count() == 0) |
+ RemovePage(p); |
+ return; |
+ } |
+ } |
+ } |
+} |
+ |
+void AppListModel::DeleteItemAt(size_t page_idx, size_t item_idx) { |
+ AppItems* apps = app_items(page_idx); |
+ apps->DeleteAt(item_idx); |
+ if (apps->item_count() == 0) |
+ RemovePage(page_idx); |
+} |
+ |
+void AppListModel::MoveItem(size_t page_idx, size_t item_idx, |
+ size_t target_page_idx, size_t target_item_idx) { |
+ VLOG(2) << "MoveItem: " << page_idx << ", " << item_idx |
+ << " -> " << target_page_idx << ", " << target_item_idx; |
+ CHECK_LT(page_idx, item_pages_.size()); |
+ CHECK_LE(item_idx, app_items(page_idx)->item_count()); |
+ if (target_page_idx == page_idx) { |
+ CHECK_LT(target_item_idx, app_items(page_idx)->item_count()); |
+ app_items(page_idx)->Move(item_idx, target_item_idx); |
+ // SetItemPosition will get called by ItemPage::ListItemMoved. |
+ } else { |
+ AppListItemModel* item = app_items(page_idx)->RemoveAt(item_idx); |
+ if (target_page_idx >= item_pages_.size()) { |
+ AddPage(); |
+ CHECK_LT(target_page_idx, item_pages_.size()); |
} |
+ CHECK_LE(target_item_idx, app_items(target_page_idx)->item_count()); |
+ AddItemToPageAtIndex(item, target_page_idx, target_item_idx); |
+ } |
+} |
+ |
+// static |
+size_t AppListModel::GetNumAppsPerPage() { |
+ return kPreferredCols * kPreferredRows; |
+} |
+ |
+size_t AppListModel::GetNumAppPages() const { |
+ return item_pages_.size(); |
+} |
+ |
+const AppListModel::AppItems& AppListModel::GetAppItemsForPage( |
+ size_t page_idx) const { |
+ CHECK_LT(page_idx, item_pages_.size()); |
+ return *(item_pages_[page_idx]->app_items()); |
+} |
+ |
+AppListModel::AppItems* AppListModel::app_items(size_t page_idx) { |
+ CHECK_LT(page_idx, item_pages_.size()); |
+ return item_pages_[page_idx]->app_items(); |
+} |
+ |
+void AppListModel::AddPage() { |
+ size_t page_idx = item_pages_.size(); |
+ item_pages_.push_back(new ItemPage(this, page_idx)); |
+ // Notify page added |
+} |
+ |
+void AppListModel::RemovePage(size_t page_idx) { |
+ item_pages_.erase(item_pages_.begin() + page_idx); |
+ // Notify page removed |
+} |
+ |
+size_t AppListModel::AddItemToPageAtIndex(AppListItemModel* item, |
+ size_t page_idx, size_t item_idx) { |
+ if (item_idx >= GetNumAppsPerPage()) { |
+ ++page_idx; |
+ item_idx = 0; |
+ } |
+ if (page_idx >= item_pages_.size()) |
+ AddPage(); |
+ |
+ AppItems* apps = app_items(page_idx); |
+ apps->AddAt(item_idx, item); |
+ VLOG(2) << "AddItemToPage: " << item->id() |
+ << " Page: " << page_idx << " Idx: " << item_idx |
+ << " / " << apps->item_count(); |
+ SetItemPosition(item, page_idx, item_idx); |
+ if (apps->item_count() > GetNumAppsPerPage()) { |
+ AppListItemModel* last_item = apps->RemoveAt(apps->item_count() - 1); |
+ CHECK_EQ(GetNumAppsPerPage(), apps->item_count()); |
+ if (++page_idx >= item_pages_.size()) |
+ AddPage(); |
+ AddItemToPageAtIndex(last_item, page_idx, 0); |
+ } |
+ return page_idx; |
+} |
+ |
+void AppListModel::SetItemPosition(AppListItemModel* item, |
+ size_t page_idx, size_t item_idx) { |
+ VLOG(2) << " SetItemPosition: " << item->id() |
+ << " Page: " << page_idx << " Idx: " << item_idx; |
+ AppItems* apps = app_items(page_idx); |
+ AppListItemModel* next = NULL; |
+ if (item_idx + 1 < apps->item_count()) { |
+ next = apps->GetItemAt(item_idx + 1); |
+ } else if (page_idx < item_pages_.size() - 1) { |
+ AppItems* next_apps = app_items(page_idx + 1); |
+ CHECK(next_apps->item_count() > 0); |
+ next = next_apps->GetItemAt(0); |
+ } |
+ AppListItemModel* prev = NULL; |
+ if (item_idx > 0) { |
+ prev = apps->GetItemAt(item_idx - 1); |
+ } else if (page_idx > 0) { |
+ AppItems* prev_apps = app_items(page_idx - 1); |
+ CHECK(prev_apps->item_count() > 0); |
+ prev = prev_apps->GetItemAt(prev_apps->item_count() - 1); |
+ } |
+ syncer::StringOrdinal new_position; |
+ if (prev) { |
+ new_position = next ? prev->position().CreateBetween(next->position()) |
+ : prev->position().CreateAfter(); |
+ } else { |
+ new_position = next ? next->position().CreateBefore() |
+ : syncer::StringOrdinal::CreateInitialOrdinal(); |
} |
+ item->set_position(new_position); |
} |
} // namespace app_list |