diff options
author | stevenjb@chromium.org <stevenjb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-30 21:09:31 +0000 |
---|---|---|
committer | stevenjb@chromium.org <stevenjb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-30 21:09:31 +0000 |
commit | ae976573b41eec9ded0fe4354a4dd1406341da5a (patch) | |
tree | 8164ab8f8646057bc3fb98bba915182e6ab51539 | |
parent | cfee2746954c8c0eb0fe3957e7f398ad97e766fa (diff) | |
download | chromium_src-ae976573b41eec9ded0fe4354a4dd1406341da5a.zip chromium_src-ae976573b41eec9ded0fe4354a4dd1406341da5a.tar.gz chromium_src-ae976573b41eec9ded0fe4354a4dd1406341da5a.tar.bz2 |
Add |position| to AppListItemModel and add AppListItemList (Take 2)
This adds a |position_| member to AppListItemModel in preparation for
syncing the app list independent of extensions.
It also moves some common code in AppListModel and AppListFolderItem
into AppListItemList, which ensures that |position_| is keept up to date.
This implementation keeps all position maintenance in AppListItemList
and uses AppListItemListObserver to allow ExtensonAppItem to
(temporarily) override the positioning logic.
BUG=305024
For as/shell OWNER:
R=jennyz@chromium.org, xiyuan@chromium.org
TBR=jamescook@chromium.org
Review URL: https://codereview.chromium.org/50003002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@231923 0039d316-1c4b-4281-b951-d872f2087c98
33 files changed, 705 insertions, 339 deletions
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc index 7448de4..fe62d21 100644 --- a/ash/shell/app_list.cc +++ b/ash/shell/app_list.cc @@ -16,6 +16,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "ui/app_list/app_list_item_list.h" #include "ui/app_list/app_list_item_model.h" #include "ui/app_list/app_list_model.h" #include "ui/app_list/app_list_view_delegate.h" @@ -195,14 +196,14 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate { ExampleAppListViewDelegate() : model_(NULL) {} private: - void PopulateApps(app_list::AppListModel::Apps* apps) { + void PopulateApps(app_list::AppListItemList* item_list) { for (int i = 0; i < static_cast<int>(WindowTypeLauncherItem::LAST_TYPE); ++i) { WindowTypeLauncherItem::Type type = static_cast<WindowTypeLauncherItem::Type>(i); std::string id = base::StringPrintf("%d", i); - apps->Add(new WindowTypeLauncherItem(id, type)); + item_list->AddItem(new WindowTypeLauncherItem(id, type)); } } @@ -233,7 +234,7 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate { virtual void InitModel(app_list::AppListModel* model) OVERRIDE { model_ = model; - PopulateApps(model_->apps()); + PopulateApps(model_->item_list()); DecorateSearchBox(model_->search_box()); } diff --git a/chrome/browser/ui/app_list/extension_app_item.cc b/chrome/browser/ui/app_list/extension_app_item.cc index 20d3f1c..dbeaa11 100644 --- a/chrome/browser/ui/app_list/extension_app_item.cc +++ b/chrome/browser/ui/app_list/extension_app_item.cc @@ -85,13 +85,7 @@ ExtensionAppItem::ExtensionAppItem(Profile* profile, Reload(); GetExtensionSorting(profile_)->EnsureValidOrdinals(extension_id_, syncer::StringOrdinal()); - // Set |sort_order_|. Use '_' as a separator to ensure a_a preceeds aa_a. - // (StringOrdinal uses 'a'-'z'). - const syncer::StringOrdinal& page = - GetExtensionSorting(profile_)->GetPageOrdinal(extension_id_); - const syncer::StringOrdinal& launch = - GetExtensionSorting(profile_)->GetAppLaunchOrdinal(extension_id_); - sort_order_ = page.ToInternalValue() + '_' + launch.ToInternalValue(); + UpdatePositionFromExtensionOrdering(); } ExtensionAppItem::~ExtensionAppItem() { @@ -169,6 +163,7 @@ void ExtensionAppItem::Move(const ExtensionAppItem* prev, service->extension_prefs()->SetAppDraggedByUser(extension_id_); sorting->SetPageOrdinal(extension_id_, page); service->OnExtensionMoved(extension_id_, prev_id, next_id); + UpdatePositionFromExtensionOrdering(); } const Extension* ExtensionAppItem::GetExtension() const { @@ -240,10 +235,6 @@ void ExtensionAppItem::ExtensionEnableFlowAborted(bool user_initiated) { controller_->OnCloseExtensionPrompt(); } -std::string ExtensionAppItem::GetSortOrder() const { - return sort_order_; -} - void ExtensionAppItem::Activate(int event_flags) { // |extension| could be NULL when it is being unloaded for updating. const Extension* extension = GetExtension(); @@ -280,3 +271,12 @@ const char* ExtensionAppItem::GetAppType() const { void ExtensionAppItem::ExecuteLaunchCommand(int event_flags) { Launch(event_flags); } + +void ExtensionAppItem::UpdatePositionFromExtensionOrdering() { + const syncer::StringOrdinal& page = + GetExtensionSorting(profile_)->GetPageOrdinal(extension_id_); + const syncer::StringOrdinal& launch = + GetExtensionSorting(profile_)->GetAppLaunchOrdinal(extension_id_); + set_position(syncer::StringOrdinal( + page.ToInternalValue() + launch.ToInternalValue())); +} diff --git a/chrome/browser/ui/app_list/extension_app_item.h b/chrome/browser/ui/app_list/extension_app_item.h index eb56f42..362a016 100644 --- a/chrome/browser/ui/app_list/extension_app_item.h +++ b/chrome/browser/ui/app_list/extension_app_item.h @@ -85,7 +85,6 @@ class ExtensionAppItem : public app_list::AppListItemModel, virtual void ExtensionEnableFlowAborted(bool user_initiated) OVERRIDE; // Overridden from AppListItemModel: - virtual std::string GetSortOrder() const OVERRIDE; virtual void Activate(int event_flags) OVERRIDE; virtual ui::MenuModel* GetContextMenuModel() OVERRIDE; virtual const char* GetAppType() const OVERRIDE; @@ -93,6 +92,9 @@ class ExtensionAppItem : public app_list::AppListItemModel, // Overridden from app_list::AppContextMenuDelegate: virtual void ExecuteLaunchCommand(int event_flags) OVERRIDE; + // Set the position from the extension ordering. + void UpdatePositionFromExtensionOrdering(); + Profile* profile_; const std::string extension_id_; AppListControllerDelegate* controller_; @@ -110,11 +112,6 @@ class ExtensionAppItem : public app_list::AppListItemModel, // Whether or not this app is a platform app. bool is_platform_app_; - // Cache initial sort order. Sort order is not synced with the extensions - // app ordering once apps are loaded. This will be ignored (or overridden) - // once the app list is synced. - std::string sort_order_; - DISALLOW_COPY_AND_ASSIGN(ExtensionAppItem); }; diff --git a/chrome/browser/ui/app_list/extension_app_model_builder.cc b/chrome/browser/ui/app_list/extension_app_model_builder.cc index 1f17f6d..63e29a8 100644 --- a/chrome/browser/ui/app_list/extension_app_model_builder.cc +++ b/chrome/browser/ui/app_list/extension_app_model_builder.cc @@ -48,13 +48,13 @@ ExtensionAppModelBuilder::ExtensionAppModelBuilder( model_(model), highlighted_app_pending_(false), tracker_(NULL) { - model_->apps()->AddObserver(this); + model_->item_list()->AddObserver(this); SwitchProfile(profile); // Builds the model. } ExtensionAppModelBuilder::~ExtensionAppModelBuilder() { OnShutdown(); - model_->apps()->RemoveObserver(this); + model_->item_list()->RemoveObserver(this); } void ExtensionAppModelBuilder::OnBeginExtensionInstall( @@ -123,7 +123,7 @@ void ExtensionAppModelBuilder::OnExtensionUnloaded(const Extension* extension) { void ExtensionAppModelBuilder::OnExtensionUninstalled( const Extension* extension) { - model_->DeleteItem(extension->id()); + model_->item_list()->DeleteItem(extension->id()); } void ExtensionAppModelBuilder::OnAppsReordered() { @@ -162,12 +162,7 @@ void ExtensionAppModelBuilder::SwitchProfile(Profile* profile) { profile_ = profile; // Delete any extension apps. - app_list::AppListModel::Apps* app_list = model_->apps(); - for (int i = static_cast<int>(app_list->item_count()) - 1; i >= 0; --i) { - app_list::AppListItemModel* item = app_list->GetItemAt(i); - if (item->GetAppType() == ExtensionAppItem::kAppType) - app_list->DeleteAt(i); - } + model_->item_list()->DeleteItemsByType(ExtensionAppItem::kAppType); if (tracker_) tracker_->RemoveObserver(this); @@ -204,7 +199,7 @@ void ExtensionAppModelBuilder::PopulateApps() { } void ExtensionAppModelBuilder::InsertApp(ExtensionAppItem* app) { - model_->AddItem(app); + model_->item_list()->AddItem(app); } void ExtensionAppModelBuilder::SetHighlightedApp( @@ -223,7 +218,8 @@ void ExtensionAppModelBuilder::SetHighlightedApp( ExtensionAppItem* ExtensionAppModelBuilder::GetExtensionAppItem( const std::string& extension_id) { - app_list::AppListItemModel* item = model_->FindItem(extension_id); + app_list::AppListItemModel* item = + model_->item_list()->FindItem(extension_id); LOG_IF(ERROR, item && item->GetAppType() != ExtensionAppItem::kAppType) << "App Item matching id: " << extension_id @@ -242,39 +238,33 @@ void ExtensionAppModelBuilder::UpdateHighlight() { highlighted_app_pending_ = false; } -void ExtensionAppModelBuilder::ListItemsAdded(size_t start, size_t count) { -} - -void ExtensionAppModelBuilder::ListItemsRemoved(size_t start, size_t count) { -} - -void ExtensionAppModelBuilder::ListItemMoved(size_t index, - size_t target_index) { - app_list::AppListModel::Apps* app_list = model_->apps(); - app_list::AppListItemModel* item = app_list->GetItemAt(target_index); +void ExtensionAppModelBuilder::OnListItemMoved( + size_t from_index, + size_t to_index, + app_list::AppListItemModel* item) { + // This will get called from AppListItemList::ListItemMoved after + // set_position is called for the item. + app_list::AppListItemList* item_list = model_->item_list(); if (item->GetAppType() != ExtensionAppItem::kAppType) return; ExtensionAppItem* prev = NULL; - for (size_t idx = target_index; idx > 1; --idx) { - app_list::AppListItemModel* item = app_list->GetItemAt(idx - 1); + for (size_t idx = to_index; idx > 0; --idx) { + app_list::AppListItemModel* item = item_list->item_at(idx - 1); if (item->GetAppType() == ExtensionAppItem::kAppType) { prev = static_cast<ExtensionAppItem*>(item); break; } } ExtensionAppItem* next = NULL; - for (size_t idx = target_index; idx < app_list->item_count() - 1; ++idx) { - app_list::AppListItemModel* item = app_list->GetItemAt(idx + 1); + for (size_t idx = to_index; idx < item_list->item_count() - 1; ++idx) { + app_list::AppListItemModel* item = item_list->item_at(idx + 1); if (item->GetAppType() == ExtensionAppItem::kAppType) { next = static_cast<ExtensionAppItem*>(item); break; } } + // item->Move will call set_position, overriding the item's position. if (prev || next) static_cast<ExtensionAppItem*>(item)->Move(prev, next); } - -void ExtensionAppModelBuilder::ListItemsChanged(size_t start, size_t count) { - NOTREACHED(); -} diff --git a/chrome/browser/ui/app_list/extension_app_model_builder.h b/chrome/browser/ui/app_list/extension_app_model_builder.h index 50cfb9e..48452ee 100644 --- a/chrome/browser/ui/app_list/extension_app_model_builder.h +++ b/chrome/browser/ui/app_list/extension_app_model_builder.h @@ -30,7 +30,7 @@ class ImageSkia; // This class populates and maintains the given |model| with information from // |profile|. class ExtensionAppModelBuilder : public extensions::InstallObserver, - public ui::ListModelObserver { + public app_list::AppListItemListObserver { public: ExtensionAppModelBuilder(Profile* profile, app_list::AppListModel* model, @@ -65,11 +65,10 @@ class ExtensionAppModelBuilder : public extensions::InstallObserver, const std::string& extension_id) OVERRIDE; virtual void OnShutdown() OVERRIDE; - // ui::ListModelObserver - virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE; - virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE; - virtual void ListItemMoved(size_t index, size_t target_index) OVERRIDE; - virtual void ListItemsChanged(size_t start, size_t count) OVERRIDE; + // AppListItemListObserver + virtual void OnListItemMoved(size_t from_index, + size_t to_index, + app_list::AppListItemModel* item) OVERRIDE; // Adds apps in |extensions| to |apps|. void AddApps(const ExtensionSet* extensions, ExtensionAppList* apps); diff --git a/chrome/browser/ui/app_list/extension_app_model_builder_unittest.cc b/chrome/browser/ui/app_list/extension_app_model_builder_unittest.cc index bfd3cdd..2354c83 100644 --- a/chrome/browser/ui/app_list/extension_app_model_builder_unittest.cc +++ b/chrome/browser/ui/app_list/extension_app_model_builder_unittest.cc @@ -32,10 +32,10 @@ const char kPackagedApp2Id[] = "jlklkagmeajbjiobondfhiekepofmljl"; // Get a string of all apps in |model| joined with ','. std::string GetModelContent(app_list::AppListModel* model) { std::string content; - for (size_t i = 0; i < model->apps()->item_count(); ++i) { + for (size_t i = 0; i < model->item_list()->item_count(); ++i) { if (i > 0) content += ','; - content += model->apps()->GetItemAt(i)->title(); + content += model->item_list()->item_at(i)->title(); } return content; } @@ -285,15 +285,15 @@ TEST_F(ExtensionAppModelBuilderTest, OrdinalConfilicts) { TEST_F(ExtensionAppModelBuilderTest, SwitchProfile) { ExtensionAppModelBuilder builder(profile_.get(), model_.get(), NULL); - EXPECT_EQ(kDefaultAppCount, model_->apps()->item_count()); + EXPECT_EQ(kDefaultAppCount, model_->item_list()->item_count()); // Switch to a profile with no apps, ensure all apps are removed. TestingProfile::Builder profile_builder; scoped_ptr<TestingProfile> profile2(profile_builder.Build()); builder.SwitchProfile(profile2.get()); - EXPECT_EQ(0u, model_->apps()->item_count()); + EXPECT_EQ(0u, model_->item_list()->item_count()); // Switch back to the main profile, ensure apps are restored. builder.SwitchProfile(profile_.get()); - EXPECT_EQ(kDefaultAppCount, model_->apps()->item_count()); + EXPECT_EQ(kDefaultAppCount, model_->item_list()->item_count()); } diff --git a/chrome/browser/ui/app_list/fast_show_pickler.cc b/chrome/browser/ui/app_list/fast_show_pickler.cc index 80b2c0f..0119b03 100644 --- a/chrome/browser/ui/app_list/fast_show_pickler.cc +++ b/chrome/browser/ui/app_list/fast_show_pickler.cc @@ -207,23 +207,23 @@ scoped_ptr<Pickle> FastShowPickler::PickleAppListModelForFastShow( return scoped_ptr<Pickle>(); if (!result->WriteBool(model->signed_in())) return scoped_ptr<Pickle>(); - if (!result->WriteInt((int) model->apps()->item_count())) + if (!result->WriteInt((int) model->item_list()->item_count())) return scoped_ptr<Pickle>(); - for (size_t i = 0; i < model->apps()->item_count(); ++i) { - if (!PickleAppListItemModel(result.get(), model->apps()->GetItemAt(i))) + for (size_t i = 0; i < model->item_list()->item_count(); ++i) { + if (!PickleAppListItemModel(result.get(), model->item_list()->item_at(i))) return scoped_ptr<Pickle>(); } return result.Pass(); } void FastShowPickler::CopyOver(AppListModel* src, AppListModel* dest) { - dest->apps()->DeleteAll(); + dest->item_list()->DeleteItemsByType(NULL /* all items */); dest->SetSignedIn(src->signed_in()); - for (size_t i = 0; i < src->apps()->item_count(); i++) { - AppListItemModel* src_item = src->apps()->GetItemAt(i); + for (size_t i = 0; i < src->item_list()->item_count(); i++) { + AppListItemModel* src_item = src->item_list()->item_at(i); AppListItemModel* dest_item = new AppListItemModel(src_item->id()); CopyOverItem(src_item, dest_item); - dest->apps()->Add(dest_item); + dest->item_list()->AddItem(dest_item); } } @@ -248,7 +248,7 @@ FastShowPickler::UnpickleAppListModelForFastShow(Pickle* pickle) { scoped_ptr<AppListItemModel> item(UnpickleAppListItemModel(&it).Pass()); if (!item) return scoped_ptr<AppListModel>(); - model->apps()->Add(item.release()); + model->item_list()->AddItem(item.release()); } return model.Pass(); diff --git a/chrome/browser/ui/app_list/test/fast_show_pickler_unittest.cc b/chrome/browser/ui/app_list/test/fast_show_pickler_unittest.cc index e87fe89..580c3a5 100644 --- a/chrome/browser/ui/app_list/test/fast_show_pickler_unittest.cc +++ b/chrome/browser/ui/app_list/test/fast_show_pickler_unittest.cc @@ -17,17 +17,17 @@ using app_list::AppListModel; class AppListModelPicklerUnitTest : public testing::Test { protected: void CheckIsSame(AppListModel* m1, AppListModel* m2) { - ASSERT_EQ(m1->apps()->item_count(), m2->apps()->item_count()); + ASSERT_EQ(m1->item_list()->item_count(), m2->item_list()->item_count()); ASSERT_EQ(m1->signed_in(), m2->signed_in()); - for (size_t i = 0; i < m1->apps()->item_count(); i++) { - ASSERT_EQ(m1->apps()->GetItemAt(i)->id(), - m2->apps()->GetItemAt(i)->id()); - ASSERT_EQ(m1->apps()->GetItemAt(i)->title(), - m2->apps()->GetItemAt(i)->title()); - ASSERT_EQ(m1->apps()->GetItemAt(i)->full_name(), - m2->apps()->GetItemAt(i)->full_name()); - CompareImages(m1->apps()->GetItemAt(i)->icon(), - m2->apps()->GetItemAt(i)->icon()); + for (size_t i = 0; i < m1->item_list()->item_count(); i++) { + ASSERT_EQ(m1->item_list()->item_at(i)->id(), + m2->item_list()->item_at(i)->id()); + ASSERT_EQ(m1->item_list()->item_at(i)->title(), + m2->item_list()->item_at(i)->title()); + ASSERT_EQ(m1->item_list()->item_at(i)->full_name(), + m2->item_list()->item_at(i)->full_name()); + CompareImages(m1->item_list()->item_at(i)->icon(), + m2->item_list()->item_at(i)->icon()); } } @@ -79,7 +79,7 @@ TEST_F(AppListModelPicklerUnitTest, OneItem) { AppListModel model; AppListItemModel* app1 = new AppListItemModel("abc"); app1->SetTitleAndFullName("ht", "hello, there"); - model.apps()->Add(app1); + model.item_list()->AddItem(app1); DoConsistencyChecks(&model); } @@ -88,11 +88,11 @@ TEST_F(AppListModelPicklerUnitTest, TwoItems) { AppListModel model; AppListItemModel* app1 = new AppListItemModel("abc"); app1->SetTitleAndFullName("ht", "hello, there"); - model.apps()->Add(app1); + model.item_list()->AddItem(app1); AppListItemModel* app2 = new AppListItemModel("abc2"); app2->SetTitleAndFullName("ht2", "hello, there 2"); - model.apps()->Add(app2); + model.item_list()->AddItem(app2); DoConsistencyChecks(&model); } @@ -102,11 +102,11 @@ TEST_F(AppListModelPicklerUnitTest, Images) { AppListItemModel* app1 = new AppListItemModel("abc"); app1->SetTitleAndFullName("ht", "hello, there"); app1->SetIcon(MakeImage(), true); - model.apps()->Add(app1); + model.item_list()->AddItem(app1); AppListItemModel* app2 = new AppListItemModel("abc2"); app2->SetTitleAndFullName("ht2", "hello, there 2"); - model.apps()->Add(app2); + model.item_list()->AddItem(app2); DoConsistencyChecks(&model); } @@ -116,7 +116,7 @@ TEST_F(AppListModelPicklerUnitTest, EmptyImage) { AppListItemModel* app1 = new AppListItemModel("abc"); app1->SetTitleAndFullName("ht", "hello, there"); app1->SetIcon(gfx::ImageSkia(), true); - model.apps()->Add(app1); + model.item_list()->AddItem(app1); DoConsistencyChecks(&model); } diff --git a/ui/app_list/DEPS b/ui/app_list/DEPS index bfb1162..bd3bf14 100644 --- a/ui/app_list/DEPS +++ b/ui/app_list/DEPS @@ -2,6 +2,7 @@ include_rules = [ "+grit/ui_resources.h", "+grit/ui_strings.h", "+skia", + "+sync", "+third_party/skia", # Allow inclusion of third-party code: "+third_party/GTM", # Google Toolbox for Mac. diff --git a/ui/app_list/app_list.gyp b/ui/app_list/app_list.gyp index 90864a7..15bd6e1 100644 --- a/ui/app_list/app_list.gyp +++ b/ui/app_list/app_list.gyp @@ -31,6 +31,9 @@ 'app_list_export.h', 'app_list_folder_item.cc', 'app_list_folder_item.h', + 'app_list_item_list.cc', + 'app_list_item_list.h', + 'app_list_item_list_observer.h', 'app_list_item_model.cc', 'app_list_item_model.h', 'app_list_item_model_observer.h', diff --git a/ui/app_list/app_list_folder_item.cc b/ui/app_list/app_list_folder_item.cc index 95e5530..e7c505a 100644 --- a/ui/app_list/app_list_folder_item.cc +++ b/ui/app_list/app_list_folder_item.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "ui/app_list/app_list_folder_item.h" + +#include "ui/app_list/app_list_item_list.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/canvas_image_source.h" #include "ui/gfx/image/image_skia_operations.h" @@ -91,38 +93,14 @@ class FolderImageSource : public gfx::CanvasImageSource { AppListFolderItem::AppListFolderItem(const std::string& id) : AppListItemModel(id), - apps_(new Apps) { + item_list_(new AppListItemList) { + item_list_->AddObserver(this); } AppListFolderItem::~AppListFolderItem() { for (size_t i = 0; i < top_items_.size(); ++i) top_items_[i]->RemoveObserver(this); -} - -void AppListFolderItem::AddItem(AppListItemModel* item) { - 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()) - break; - } - apps_->AddAt(index, item); - if (index <= kNumTopApps) - UpdateTopItems(); -} - -void AppListFolderItem::DeleteItem(const std::string& id) { - for (size_t i = 0; i < apps_->item_count(); ++i) { - AppListItemModel* item = apps_->GetItemAt(i); - if (item->id() == id) { - scoped_ptr<AppListItemModel> to_be_deleted(apps_->RemoveAt(i)); - DCHECK(item == to_be_deleted.get()); - if (i <= kNumTopApps) - UpdateTopItems(); - return; - } - } + item_list_->RemoveObserver(this); } void AppListFolderItem::UpdateIcon() { @@ -137,12 +115,6 @@ void AppListFolderItem::UpdateIcon() { SetIcon(icon, false); } -std::string AppListFolderItem::GetSortOrder() const { - // For now, put folders at the end of the list. - // TODO(stevenjb): Implement synced app list ordering. - return "zzzzzzzz"; -} - void AppListFolderItem::Activate(int event_flags) { // Folder handling is implemented by the View, so do nothing. } @@ -175,13 +147,33 @@ void AppListFolderItem::ItemIsInstallingChanged() { void AppListFolderItem::ItemPercentDownloadedChanged() { } +void AppListFolderItem::OnListItemAdded(size_t index, + AppListItemModel* item) { + if (index <= kNumTopApps) + UpdateTopItems(); +} + +void AppListFolderItem::OnListItemRemoved(size_t index, + AppListItemModel* item) { + if (index <= kNumTopApps) + UpdateTopItems(); +} + +void AppListFolderItem::OnListItemMoved(size_t from_index, + size_t to_index, + AppListItemModel* item) { + if (from_index <= kNumTopApps || to_index <= kNumTopApps) + UpdateTopItems(); +} + void AppListFolderItem::UpdateTopItems() { for (size_t i = 0; i < top_items_.size(); ++i) top_items_[i]->RemoveObserver(this); top_items_.clear(); - for (size_t i = 0; i < kNumTopApps && i < apps_->item_count(); ++i) { - AppListItemModel* item = apps_->GetItemAt(i); + for (size_t i = 0; + i < kNumTopApps && i < item_list_->item_count(); ++i) { + AppListItemModel* item = item_list_->item_at(i); item->AddObserver(this); top_items_.push_back(item); } diff --git a/ui/app_list/app_list_folder_item.h b/ui/app_list/app_list_folder_item.h index db078ea..dd2ce7b 100644 --- a/ui/app_list/app_list_folder_item.h +++ b/ui/app_list/app_list_folder_item.h @@ -6,55 +6,56 @@ #define UI_APP_LIST_APP_LIST_FOLDER_ITEM_H_ #include "ui/app_list/app_list_export.h" +#include "ui/app_list/app_list_item_list_observer.h" #include "ui/app_list/app_list_item_model.h" #include "ui/app_list/app_list_item_model_observer.h" #include "ui/base/models/list_model.h" namespace app_list { +class AppListItemList; + // AppListFolderItem implements the model/controller for folders. class APP_LIST_EXPORT AppListFolderItem : public AppListItemModel, + public AppListItemListObserver, public AppListItemModelObserver { public: - typedef ui::ListModel<AppListItemModel> Apps; - explicit AppListFolderItem(const std::string& id); virtual ~AppListFolderItem(); - // Adds |item| to |apps_|. Takes ownership of |item|. - void AddItem(AppListItemModel* item); - - // Finds |item| in |apps_| and deletes it. - void DeleteItem(const std::string& id); - // Updates the folder's icon. void UpdateIcon(); - Apps* apps() { return apps_.get(); } + AppListItemList* item_list() { return item_list_.get(); } + + static const char kAppType[]; + private: // AppListItemModel - virtual std::string GetSortOrder() const OVERRIDE; virtual void Activate(int event_flags) OVERRIDE; virtual const char* GetAppType() const OVERRIDE; virtual ui::MenuModel* GetContextMenuModel() OVERRIDE; - // AppListItemModelObserver overrides: + // AppListItemModelObserver virtual void ItemIconChanged() OVERRIDE; virtual void ItemTitleChanged() OVERRIDE; virtual void ItemHighlightedChanged() OVERRIDE; virtual void ItemIsInstallingChanged() OVERRIDE; virtual void ItemPercentDownloadedChanged() OVERRIDE; - static const char kAppType[]; - - private: - typedef std::vector<AppListItemModel*> AppListItemList; + // AppListItemListObserver + virtual void OnListItemAdded(size_t index, AppListItemModel* item) OVERRIDE; + virtual void OnListItemRemoved(size_t index, + AppListItemModel* item) OVERRIDE;; + virtual void OnListItemMoved(size_t from_index, + size_t to_index, + AppListItemModel* item) OVERRIDE; void UpdateTopItems(); - scoped_ptr<Apps> apps_; + scoped_ptr<AppListItemList> item_list_; // Top items for generating folder icon. - AppListItemList top_items_; + std::vector<AppListItemModel*> top_items_; DISALLOW_COPY_AND_ASSIGN(AppListFolderItem); }; diff --git a/ui/app_list/app_list_item_list.cc b/ui/app_list/app_list_item_list.cc new file mode 100644 index 0000000..29578f2 --- /dev/null +++ b/ui/app_list/app_list_item_list.cc @@ -0,0 +1,132 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/app_list/app_list_item_list.h" + +#include "ui/app_list/app_list_item_model.h" + +namespace app_list { + +AppListItemList::AppListItemList() { +} + +AppListItemList::~AppListItemList() { +} + +void AppListItemList::AddObserver(AppListItemListObserver* observer) { + observers_.AddObserver(observer); +} + +void AppListItemList::RemoveObserver(AppListItemListObserver* observer) { + observers_.RemoveObserver(observer); +} + +AppListItemModel* AppListItemList::FindItem(const std::string& id) { + for (size_t i = 0; i < app_list_items_.size(); ++i) { + AppListItemModel* item = app_list_items_[i]; + if (item->id() == id) + return item; + } + return NULL; +} + +size_t AppListItemList::AddItem(AppListItemModel* item) { + size_t index = GetItemSortOrderIndex(item); + app_list_items_.insert(app_list_items_.begin() + index, item); + FOR_EACH_OBSERVER(AppListItemListObserver, + observers_, + OnListItemAdded(index, item)); + return index; +} + +void AppListItemList::DeleteItem(const std::string& id) { + for (size_t i = 0; i < app_list_items_.size(); ++i) { + AppListItemModel* item = app_list_items_[i]; + if (item->id() == id) { + DeleteItemAt(i); + break; + } + } +} + +void AppListItemList::DeleteItemsByType(const char* type) { + for (int i = static_cast<int>(app_list_items_.size()) - 1; + i >= 0; --i) { + AppListItemModel* item = app_list_items_[i]; + if (!type || item->GetAppType() == type) + DeleteItemAt(i); + } +} + +void AppListItemList::MoveItem(size_t from_index, size_t to_index) { + DCHECK_LT(from_index, item_count()); + DCHECK_LT(to_index, item_count()); + if (from_index == to_index) + return; + + AppListItemModel* target_item = app_list_items_[from_index]; + app_list_items_.weak_erase(app_list_items_.begin() + from_index); + app_list_items_.insert(app_list_items_.begin() + to_index, target_item); + + // Update position + AppListItemModel* prev = to_index > 0 ? app_list_items_[to_index - 1] : NULL; + AppListItemModel* next = to_index < app_list_items_.size() - 1 ? + app_list_items_[to_index + 1] : NULL; + CHECK_NE(prev, next); + + // It is possible that items were added with the same ordinal. Rather than + // resolving a potentially complicated chain of conflicts, just set the + // ordinal before |next| (which will place it before both items). + if (prev && next && prev->position().Equals(next->position())) + prev = NULL; + + VLOG(2) << "Move: " << target_item->position().ToDebugString() + << " Prev: " << (prev ? prev->position().ToDebugString() : "(none)") + << " Next: " << (next ? next->position().ToDebugString() : "(none)"); + if (!prev) + target_item->set_position(next->position().CreateBefore()); + else if (!next) + target_item->set_position(prev->position().CreateAfter()); + else + target_item->set_position(prev->position().CreateBetween(next->position())); + FOR_EACH_OBSERVER(AppListItemListObserver, + observers_, + OnListItemMoved(from_index, to_index, target_item)); +} + +// AppListItemList private + +void AppListItemList::DeleteItemAt(size_t index) { + DCHECK_LT(index, item_count()); + AppListItemModel* item = app_list_items_[index]; + app_list_items_.weak_erase(app_list_items_.begin() + index); + FOR_EACH_OBSERVER(AppListItemListObserver, + observers_, + OnListItemRemoved(index, item)); + delete item; +} + +size_t AppListItemList::GetItemSortOrderIndex(AppListItemModel* item) { + syncer::StringOrdinal position = item->position(); + if (!position.IsValid()) { + size_t nitems = app_list_items_.size(); + if (nitems == 0) { + position = syncer::StringOrdinal::CreateInitialOrdinal(); + } else { + position = app_list_items_[nitems - 1]->position().CreateAfter(); + } + item->set_position(position); + return nitems; + } + // Note: app_list_items_ is sorted by convention, but sorting is not enforced + // (items' positions might be changed outside this class). + size_t index = 0; + for (; index < app_list_items_.size(); ++index) { + if (position.LessThan(app_list_items_[index]->position())) + break; + } + return index; +} + +} // namespace app_list diff --git a/ui/app_list/app_list_item_list.h b/ui/app_list/app_list_item_list.h new file mode 100644 index 0000000..6b033c9 --- /dev/null +++ b/ui/app_list/app_list_item_list.h @@ -0,0 +1,72 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_APP_LIST_APP_LIST_ITEM_LIST_H_ +#define UI_APP_LIST_APP_LIST_ITEM_LIST_H_ + +#include <string> + +#include "base/memory/scoped_vector.h" +#include "base/observer_list.h" +#include "ui/app_list/app_list_export.h" +#include "ui/app_list/app_list_item_list_observer.h" + +namespace app_list { + +class AppListItemModel; + +// Class to manage items in the app list. Used both by AppListModel and +// AppListFolderItem. Manages the position ordinal of items in the list, and +// notifies observers when items in the list are added / deleted / moved. +class APP_LIST_EXPORT AppListItemList { + public: + AppListItemList(); + virtual ~AppListItemList(); + + void AddObserver(AppListItemListObserver* observer); + void RemoveObserver(AppListItemListObserver* observer); + + // Find item matching |id|. NOTE: Requires a linear search. + AppListItemModel* FindItem(const std::string& id); + + // Adds |item| to the end of |app_list_items_|. Takes ownership of |item|. + // Triggers observers_.OnListItemAdded(). Returns the index of the added item. + size_t AddItem(AppListItemModel* item); + + // Finds item matching |id| in |app_list_items_| (linear search) and deletes + // it. Triggers observers_.OnListItemRemoved() after removing the item from + // the list and before deleting it. + void DeleteItem(const std::string& id); + + // Deletes all items matching |type| which must be a statically defined + // type descriptor, e.g. AppListFolderItem::kAppType. If |type| is NULL, + // deletes all items. Triggers observers_.OnListItemRemoved() for each item + // as for DeleteItem. + void DeleteItemsByType(const char* type); + + // Moves item at |from_index| to |to_index|. + // Triggers observers_.OnListItemMoved(). + void MoveItem(size_t from_index, size_t to_index); + + AppListItemModel* item_at(size_t index) { return app_list_items_[index]; } + size_t item_count() const { return app_list_items_.size(); } + + private: + // Deletes item at |index| and signals observers. + void DeleteItemAt(size_t index); + + // Returns the index at which to insert |item| in |app_list_items_| based on + // |item|->position(). If |item|->position() is not valid, returns + // |app_list_items_|.item_count() and sets |item|->position() appropriately. + size_t GetItemSortOrderIndex(AppListItemModel* item); + + ScopedVector<AppListItemModel> app_list_items_; + ObserverList<AppListItemListObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(AppListItemList); +}; + +} // namespace app_list + +#endif // UI_APP_LIST_APP_LIST_ITEM_LIST_H_ diff --git a/ui/app_list/app_list_item_list_observer.h b/ui/app_list/app_list_item_list_observer.h new file mode 100644 index 0000000..bca7e90 --- /dev/null +++ b/ui/app_list/app_list_item_list_observer.h @@ -0,0 +1,34 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_APP_LIST_APP_LIST_ITEM_LIST_OBSERVER_H_ +#define UI_APP_LIST_APP_LIST_ITEM_LIST_OBSERVER_H_ + +#include "base/basictypes.h" + +namespace app_list { + +class AppListItemModel; + +class APP_LIST_EXPORT AppListItemListObserver { + public: + // Triggered after |item| has been added to the list at |index|. + virtual void OnListItemAdded(size_t index, AppListItemModel* item) {} + + // Triggered after an item has been removed from the list at |index|, just + // before the item is deleted. + virtual void OnListItemRemoved(size_t index, AppListItemModel* item) {} + + // Triggered after |item| has been moved from |from_index| to |to_index|. + virtual void OnListItemMoved(size_t from_index, + size_t to_index, + AppListItemModel* item) {} + + protected: + virtual ~AppListItemListObserver() {} +}; + +} // namespace app_list + +#endif // UI_APP_LIST_APP_LIST_ITEM_LIST_OBSERVER_H_ diff --git a/ui/app_list/app_list_item_model.cc b/ui/app_list/app_list_item_model.cc index 87b6969..1846f5e 100644 --- a/ui/app_list/app_list_item_model.cc +++ b/ui/app_list/app_list_item_model.cc @@ -73,10 +73,6 @@ void AppListItemModel::RemoveObserver(AppListItemModelObserver* observer) { observers_.RemoveObserver(observer); } -std::string AppListItemModel::GetSortOrder() const { - return ""; -} - void AppListItemModel::Activate(int event_flags) { } diff --git a/ui/app_list/app_list_item_model.h b/ui/app_list/app_list_item_model.h index 859b348..505bbb6 100644 --- a/ui/app_list/app_list_item_model.h +++ b/ui/app_list/app_list_item_model.h @@ -9,6 +9,7 @@ #include "base/basictypes.h" #include "base/observer_list.h" +#include "sync/api/string_ordinal.h" #include "ui/app_list/app_list_export.h" #include "ui/gfx/image/image_skia.h" @@ -46,14 +47,15 @@ class APP_LIST_EXPORT AppListItemModel { int percent_downloaded() const { return percent_downloaded_; } const std::string& id() const { return id_; } + const syncer::StringOrdinal& position() const { return position_; } + void set_position(const syncer::StringOrdinal& new_position) { + DCHECK(new_position.IsValid()); + position_ = new_position; + } void AddObserver(AppListItemModelObserver* observer); void RemoveObserver(AppListItemModelObserver* observer); - // Returns a string used for initially sorting the apps (used by - // AppListModel::AddItem). Defaults to an empty string. - virtual std::string GetSortOrder() const; - // Activates (opens) the item. Does nothing by default. virtual void Activate(int event_flags); @@ -67,7 +69,10 @@ class APP_LIST_EXPORT AppListItemModel { virtual ui::MenuModel* GetContextMenuModel(); private: + friend class AppListModelTest; + const std::string id_; + syncer::StringOrdinal position_; gfx::ImageSkia icon_; bool has_shadow_; std::string title_; diff --git a/ui/app_list/app_list_model.cc b/ui/app_list/app_list_model.cc index 2464656..40bb8e9 100644 --- a/ui/app_list/app_list_model.cc +++ b/ui/app_list/app_list_model.cc @@ -16,7 +16,7 @@ AppListModel::User::User() : active(false) {} AppListModel::User::~User() {} AppListModel::AppListModel() - : apps_(new Apps), + : item_list_(new AppListItemList), search_box_(new SearchBoxModel), results_(new SearchResults), signed_in_(false), @@ -61,34 +61,4 @@ void AppListModel::SetSignedIn(bool signed_in) { OnAppListModelSigninStatusChanged()); } -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; - } - return NULL; -} - -void AppListModel::AddItem(AppListItemModel* item) { - 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()) - break; - } - apps_->AddAt(index, item); -} - -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; - } - } -} - } // namespace app_list diff --git a/ui/app_list/app_list_model.h b/ui/app_list/app_list_model.h index 2238941..fbc7970 100644 --- a/ui/app_list/app_list_model.h +++ b/ui/app_list/app_list_model.h @@ -13,18 +13,20 @@ #include "base/observer_list.h" #include "base/strings/string16.h" #include "ui/app_list/app_list_export.h" +#include "ui/app_list/app_list_item_list.h" #include "ui/base/models/list_model.h" namespace app_list { +class AppListItemList; class AppListItemModel; class AppListModelObserver; class SearchBoxModel; class SearchResult; -// Master model of app list that consists of three sub models: Apps, -// SearchBoxModel and SearchResults. The Apps sub model owns a list of -// AppListItemModel and is displayed in the grid view. SearchBoxModel is +// Master model of app list that consists of three sub models: AppListItemList, +// SearchBoxModel and SearchResults. The AppListItemList sub model owns a list +// of AppListItemModel and is displayed in the grid view. SearchBoxModel is // the model for SearchBoxView. SearchResults owns a list of SearchResult. class APP_LIST_EXPORT AppListModel { public: @@ -51,7 +53,6 @@ class APP_LIST_EXPORT AppListModel { STATUS_SYNCING, // Syncing apps or installing synced apps. }; - typedef ui::ListModel<AppListItemModel> Apps; typedef ui::ListModel<SearchResult> SearchResults; typedef std::vector<User> Users; @@ -65,18 +66,7 @@ class APP_LIST_EXPORT AppListModel { void SetUsers(const Users& profile_menu_items); void SetSignedIn(bool signed_in); - // Find item matching |id|. NOTE: Requires a linear search. - AppListItemModel* FindItem(const std::string& id); - - // Adds |item| to |apps_|. Takes ownership of |item|. Uses item->SortOrder() - // to insert in the correct place. TODO(stevenjb): Use synced app list order - // instead of SortOrder when available: crbug.com/305024. - void AddItem(AppListItemModel* item); - - // Finds |item| in |apps_| and deletes it. - void DeleteItem(const std::string& id); - - Apps* apps() { return apps_.get(); } + AppListItemList* item_list() { return item_list_.get(); } SearchBoxModel* search_box() { return search_box_.get(); } SearchResults* results() { return results_.get(); } Status status() const { return status_; } @@ -87,8 +77,7 @@ class APP_LIST_EXPORT AppListModel { } private: - scoped_ptr<Apps> apps_; - + scoped_ptr<AppListItemList> item_list_; scoped_ptr<SearchBoxModel> search_box_; scoped_ptr<SearchResults> results_; diff --git a/ui/app_list/app_list_model_unittest.cc b/ui/app_list/app_list_model_unittest.cc index 02d77a4..01df3e2 100644 --- a/ui/app_list/app_list_model_unittest.cc +++ b/ui/app_list/app_list_model_unittest.cc @@ -8,6 +8,7 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/app_list/app_list_folder_item.h" #include "ui/app_list/app_list_item_model.h" #include "ui/app_list/app_list_model_observer.h" #include "ui/app_list/test/app_list_test_model.h" @@ -18,7 +19,7 @@ namespace app_list { namespace { class TestObserver : public AppListModelObserver, - public ui::ListModelObserver { + public AppListItemListObserver { public: TestObserver() : status_changed_count_(0), @@ -44,22 +45,22 @@ class TestObserver : public AppListModelObserver, ++signin_changed_count_; } - // ui::ListModelObserver - virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE { - items_added_ += count; + // AppListItemListObserver + virtual void OnListItemAdded(size_t index, AppListItemModel* item) OVERRIDE { + items_added_++; } - virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE { - items_removed_ += count; + virtual void OnListItemRemoved(size_t index, + AppListItemModel* item) OVERRIDE { + items_removed_++; } - virtual void ListItemMoved(size_t index, size_t target_index) OVERRIDE { + virtual void OnListItemMoved(size_t from_index, + size_t to_index, + AppListItemModel* item) OVERRIDE { items_moved_++; } - virtual void ListItemsChanged(size_t index, size_t target_index) OVERRIDE { - } - int status_changed_count() const { return status_changed_count_; } int users_changed_count() const { return users_changed_count_; } int signin_changed_count() const { return signin_changed_count_; } @@ -67,6 +68,15 @@ class TestObserver : public AppListModelObserver, size_t items_removed() { return items_removed_; } size_t items_moved() { return items_moved_; } + void ResetCounts() { + status_changed_count_ = 0; + users_changed_count_ = 0; + signin_changed_count_ = 0; + items_added_ = 0; + items_removed_ = 0; + items_moved_ = 0; + } + private: int status_changed_count_; int users_changed_count_; @@ -88,14 +98,19 @@ class AppListModelTest : public testing::Test { // testing::Test overrides: virtual void SetUp() OVERRIDE { model_.AddObserver(&observer_); - model_.apps()->AddObserver(&observer_); + model_.item_list()->AddObserver(&observer_); } virtual void TearDown() OVERRIDE { model_.RemoveObserver(&observer_); - model_.apps()->RemoveObserver(&observer_); + model_.item_list()->RemoveObserver(&observer_); } protected: + bool ItemObservedByFolder(AppListFolderItem* folder, + AppListItemModel* item) { + return item->observers_.HasObserver(folder); + } + test::AppListTestModel model_; TestObserver observer_; @@ -148,10 +163,10 @@ TEST_F(AppListModelTest, AppsObserver) { TEST_F(AppListModelTest, ModelGetItem) { const size_t num_apps = 2; model_.PopulateApps(num_apps); - AppListItemModel* item0 = model_.apps()->GetItemAt(0); + AppListItemModel* item0 = model_.item_list()->item_at(0); ASSERT_TRUE(item0); EXPECT_EQ(model_.GetItemName(0), item0->id()); - AppListItemModel* item1 = model_.apps()->GetItemAt(1); + AppListItemModel* item1 = model_.item_list()->item_at(1); ASSERT_TRUE(item1); EXPECT_EQ(model_.GetItemName(1), item1->id()); } @@ -160,26 +175,137 @@ TEST_F(AppListModelTest, ModelFindItem) { const size_t num_apps = 2; model_.PopulateApps(num_apps); std::string item_name0 = model_.GetItemName(0); - AppListItemModel* item0 = model_.FindItem(item_name0); + AppListItemModel* item0 = model_.item_list()->FindItem(item_name0); ASSERT_TRUE(item0); EXPECT_EQ(item_name0, item0->id()); std::string item_name1 = model_.GetItemName(1); - AppListItemModel* item1 = model_.FindItem(item_name1); + AppListItemModel* item1 = model_.item_list()->FindItem(item_name1); ASSERT_TRUE(item1); EXPECT_EQ(item_name1, item1->id()); } +TEST_F(AppListModelTest, ModelAddItem) { + const size_t num_apps = 2; + model_.PopulateApps(num_apps); + // Adding another item will add it to the end. + model_.CreateAndAddItem("Added Item 1"); + ASSERT_EQ(num_apps + 1, model_.item_list()->item_count()); + EXPECT_EQ("Added Item 1", model_.item_list()->item_at(num_apps)->id()); + // Add an item between items 0 and 1. + app_list::AppListItemModel* item0 = model_.item_list()->item_at(0); + ASSERT_TRUE(item0); + app_list::AppListItemModel* item1 = model_.item_list()->item_at(1); + ASSERT_TRUE(item1); + app_list::AppListItemModel* item2 = + model_.CreateItem("Added Item 2", "Added Item 2"); + item2->set_position(item0->position().CreateBetween(item1->position())); + model_.item_list()->AddItem(item2); + EXPECT_EQ(num_apps + 2, model_.item_list()->item_count()); + EXPECT_EQ("Added Item 2", model_.item_list()->item_at(1)->id()); +} + TEST_F(AppListModelTest, ModelMoveItem) { const size_t num_apps = 3; model_.PopulateApps(num_apps); // Adding another item will add it to the end. model_.CreateAndAddItem("Inserted Item"); - ASSERT_EQ(num_apps + 1, model_.apps()->item_count()); + ASSERT_EQ(num_apps + 1, model_.item_list()->item_count()); // Move it to the position 1. - model_.apps()->Move(num_apps, 1); - AppListItemModel* item = model_.apps()->GetItemAt(1); + model_.item_list()->MoveItem(num_apps, 1); + AppListItemModel* item = model_.item_list()->item_at(1); ASSERT_TRUE(item); EXPECT_EQ("Inserted Item", item->id()); } +TEST_F(AppListModelTest, ModelRemoveItem) { + const size_t num_apps = 4; + model_.PopulateApps(num_apps); + // Remove an item in the middle. + model_.item_list()->DeleteItem(model_.GetItemName(1)); + EXPECT_EQ(num_apps - 1, model_.item_list()->item_count()); + EXPECT_EQ(1u, observer_.items_removed()); + // Remove the first item in the list. + model_.item_list()->DeleteItem(model_.GetItemName(0)); + EXPECT_EQ(num_apps - 2, model_.item_list()->item_count()); + EXPECT_EQ(2u, observer_.items_removed()); + // Remove the last item in the list. + model_.item_list()->DeleteItem(model_.GetItemName(num_apps - 1)); + EXPECT_EQ(num_apps - 3, model_.item_list()->item_count()); + EXPECT_EQ(3u, observer_.items_removed()); + // Ensure that the first item is the expected one + AppListItemModel* item0 = model_.item_list()->item_at(0); + ASSERT_TRUE(item0); + EXPECT_EQ(model_.GetItemName(2), item0->id()); +} + +TEST_F(AppListModelTest, ModelRemoveItemByType) { + const size_t num_apps = 4; + model_.PopulateApps(num_apps); + model_.item_list()->AddItem(new AppListFolderItem("folder1")); + model_.item_list()->AddItem(new AppListFolderItem("folder2")); + model_.item_list()->DeleteItemsByType(test::AppListTestModel::kAppType); + EXPECT_EQ(num_apps, observer_.items_removed()); + EXPECT_EQ(2u, model_.item_list()->item_count()); + model_.item_list()->DeleteItemsByType(AppListFolderItem::kAppType); + EXPECT_EQ(num_apps + 2, observer_.items_removed()); + EXPECT_EQ(0u, model_.item_list()->item_count()); + // Delete all items + observer_.ResetCounts(); + model_.PopulateApps(num_apps); + model_.item_list()->AddItem(new AppListFolderItem("folder1")); + model_.item_list()->AddItem(new AppListFolderItem("folder2")); + model_.item_list()->DeleteItemsByType(NULL /* all items */); + EXPECT_EQ(num_apps + 2, observer_.items_removed()); + EXPECT_EQ(0u, model_.item_list()->item_count()); +} + +TEST_F(AppListModelTest, AppOrder) { + const size_t num_apps = 5; + model_.PopulateApps(num_apps); + // Ensure order is preserved. + for (size_t i = 1; i < num_apps; ++i) { + EXPECT_TRUE(model_.item_list()->item_at(i)->position().GreaterThan( + model_.item_list()->item_at(i - 1)->position())); + } + // Move an app + model_.item_list()->MoveItem(num_apps - 1, 1); + // Ensure order is preserved. + for (size_t i = 1; i < num_apps; ++i) { + EXPECT_TRUE(model_.item_list()->item_at(i)->position().GreaterThan( + model_.item_list()->item_at(i - 1)->position())); + } +} + +TEST_F(AppListModelTest, FolderItem) { + AppListFolderItem* folder = new AppListFolderItem("folder1"); + const size_t num_folder_apps = 8; + const size_t num_observed_apps = 4; + for (int i = 0; static_cast<size_t>(i) < num_folder_apps; ++i) { + std::string name = model_.GetItemName(i); + folder->item_list()->AddItem(model_.CreateItem(name, name)); + } + // Check that items 0 and 3 are observed. + EXPECT_TRUE(ItemObservedByFolder(folder, folder->item_list()->item_at(0))); + EXPECT_TRUE(ItemObservedByFolder( + folder, folder->item_list()->item_at(num_observed_apps - 1))); + // Check that item 4 is not observed. + EXPECT_FALSE(ItemObservedByFolder( + folder, folder->item_list()->item_at(num_observed_apps))); + folder->item_list()->MoveItem(num_observed_apps, 0); + // Confirm that everything was moved where expected. + EXPECT_EQ(model_.GetItemName(num_observed_apps), + folder->item_list()->item_at(0)->id()); + EXPECT_EQ(model_.GetItemName(0), + folder->item_list()->item_at(1)->id()); + EXPECT_EQ(model_.GetItemName(num_observed_apps - 1), + folder->item_list()->item_at(num_observed_apps)->id()); + // Check that items 0 and 3 are observed. + EXPECT_TRUE(ItemObservedByFolder(folder, folder->item_list()->item_at(0))); + EXPECT_TRUE(ItemObservedByFolder( + folder, folder->item_list()->item_at(num_observed_apps - 1))); + // Check that item 4 is not observed. + EXPECT_FALSE(ItemObservedByFolder( + folder, folder->item_list()->item_at(num_observed_apps))); +} + } // namespace app_list diff --git a/ui/app_list/cocoa/apps_grid_controller.h b/ui/app_list/cocoa/apps_grid_controller.h index 77ad382..b53c814 100644 --- a/ui/app_list/cocoa/apps_grid_controller.h +++ b/ui/app_list/cocoa/apps_grid_controller.h @@ -23,7 +23,7 @@ class AppsGridDelegateBridge; @protocol AppsPaginationModelObserver; @class AppsCollectionViewDragManager; -// Controls a grid of views, representing AppListModel::Apps sub models. +// Controls a grid of views, representing AppListItemList sub models. APP_LIST_EXPORT @interface AppsGridController : NSViewController<GestureScrollDelegate, AppListPagerDelegate, diff --git a/ui/app_list/cocoa/apps_grid_controller.mm b/ui/app_list/cocoa/apps_grid_controller.mm index 5fad26f..d450ef4 100644 --- a/ui/app_list/cocoa/apps_grid_controller.mm +++ b/ui/app_list/cocoa/apps_grid_controller.mm @@ -76,12 +76,11 @@ NSTimeInterval g_scroll_duration = 0.18; - (void)updatePageContent:(size_t)pageIndex resetModel:(BOOL)resetModel; -// Bridged methods for ui::ListModelObserver. -- (void)listItemsAdded:(size_t)start - count:(size_t)count; +// Bridged methods for AppListItemListObserver. +- (void)listItemAdded:(size_t)index + item:(app_list::AppListItemModel*)item; -- (void)listItemsRemoved:(size_t)start - count:(size_t)count; +- (void)listItemRemoved:(size_t)index; - (void)listItemMovedFromIndex:(size_t)fromIndex toModelIndex:(size_t)toIndex; @@ -93,26 +92,25 @@ NSTimeInterval g_scroll_duration = 0.18; namespace app_list { -class AppsGridDelegateBridge : public ui::ListModelObserver { +class AppsGridDelegateBridge : public AppListItemListObserver { public: AppsGridDelegateBridge(AppsGridController* parent) : parent_(parent) {} private: - // Overridden from ui::ListModelObserver: - virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE { - [parent_ listItemsAdded:start - count:count]; + // Overridden from AppListItemListObserver: + virtual void OnListItemAdded(size_t index, AppListItemModel* item) OVERRIDE { + [parent_ listItemAdded:index + item:item]; } - virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE { - [parent_ listItemsRemoved:start - count:count]; + virtual void OnListItemRemoved(size_t index, + AppListItemModel* item) OVERRIDE { + [parent_ listItemRemoved:index]; } - virtual void ListItemMoved(size_t index, size_t target_index) OVERRIDE { - [parent_ listItemMovedFromIndex:index - toModelIndex:target_index]; - } - virtual void ListItemsChanged(size_t start, size_t count) OVERRIDE { - NOTREACHED(); + virtual void OnListItemMoved(size_t from_index, + size_t to_index, + AppListItemModel* item) OVERRIDE { + [parent_ listItemMovedFromIndex:from_index + toModelIndex:to_index]; } AppsGridController* parent_; // Weak, owns us. @@ -188,7 +186,7 @@ class AppsGridDelegateBridge : public ui::ListModelObserver { - (void)setModel:(scoped_ptr<app_list::AppListModel>)newModel { if (model_) { - model_->apps()->RemoveObserver(bridge_.get()); + model_->item_list()->RemoveObserver(bridge_.get()); // Since the model is about to be deleted, and the AppKit objects might be // sitting in an NSAutoreleasePool, ensure there are no references to the @@ -205,9 +203,13 @@ class AppsGridDelegateBridge : public ui::ListModelObserver { if (!model_) return; - model_->apps()->AddObserver(bridge_.get()); - [self listItemsAdded:0 - count:model_->apps()->item_count()]; + model_->item_list()->AddObserver(bridge_.get()); + for (size_t i = 0; i < model_->item_list()->item_count(); ++i) { + app_list::AppListItemModel* itemModel = model_->item_list()->item_at(i); + [items_ insertObject:[NSValue valueWithPointer:itemModel] + atIndex:i]; + } + [self updatePages:0]; } - (void)setDelegate:(app_list::AppListViewDelegate*)newDelegate { @@ -522,9 +524,9 @@ class AppsGridDelegateBridge : public ui::ListModelObserver { if (itemIndex == modelIndex) return; - model_->apps()->RemoveObserver(bridge_.get()); - model_->apps()->Move(itemIndex, modelIndex); - model_->apps()->AddObserver(bridge_.get()); + model_->item_list()->RemoveObserver(bridge_.get()); + model_->item_list()->MoveItem(itemIndex, modelIndex); + model_->item_list()->AddObserver(bridge_.get()); } - (AppsCollectionViewDragManager*)dragManager { @@ -535,30 +537,25 @@ class AppsGridDelegateBridge : public ui::ListModelObserver { return scheduledScrollPage_; } -- (void)listItemsAdded:(size_t)start - count:(size_t)count { +- (void)listItemAdded:(size_t)index + item:(app_list::AppListItemModel*)itemModel { // Cancel any drag, to ensure the model stays consistent. [dragManager_ cancelDrag]; - for (size_t i = start; i < start + count; ++i) { - app_list::AppListItemModel* itemModel = model_->apps()->GetItemAt(i); - [items_ insertObject:[NSValue valueWithPointer:itemModel] - atIndex:i]; - } + [items_ insertObject:[NSValue valueWithPointer:itemModel] + atIndex:index]; - [self updatePages:start]; + [self updatePages:index]; } -- (void)listItemsRemoved:(size_t)start - count:(size_t)count { +- (void)listItemRemoved:(size_t)index { [dragManager_ cancelDrag]; // Clear the models explicitly to avoid surprises from autorelease. - for (size_t i = start; i < start + count; ++i) - [[self itemAtIndex:i] setModel:NULL]; + [[self itemAtIndex:index] setModel:NULL]; - [items_ removeObjectsInRange:NSMakeRange(start, count)]; - [self updatePages:start]; + [items_ removeObjectsInRange:NSMakeRange(index, 1)]; + [self updatePages:index]; } - (void)listItemMovedFromIndex:(size_t)fromIndex diff --git a/ui/app_list/cocoa/apps_grid_controller_unittest.mm b/ui/app_list/cocoa/apps_grid_controller_unittest.mm index df4e142..5692574 100644 --- a/ui/app_list/cocoa/apps_grid_controller_unittest.mm +++ b/ui/app_list/cocoa/apps_grid_controller_unittest.mm @@ -424,7 +424,7 @@ TEST_F(AppsGridControllerTest, EnsureHighlightedVisible) { // Test runtime updates: adding items, removing items, and moving items (e.g. in // response to app install, uninstall, and chrome sync changes. Also test // changing titles and icons. -TEST_F(AppsGridControllerTest, ModelUpdates) { +TEST_F(AppsGridControllerTest, ModelUpdate) { model()->PopulateApps(2); EXPECT_EQ(2u, [[GetPageAt(0) content] count]); EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent()); @@ -437,7 +437,7 @@ TEST_F(AppsGridControllerTest, ModelUpdates) { EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent()); // Update the title via the ItemModelObserver. - app_list::AppListItemModel* item_model = model()->apps()->GetItemAt(2); + app_list::AppListItemModel* item_model = model()->item_list()->item_at(2); item_model->SetTitleAndFullName("UpdatedItem", "UpdatedItem"); EXPECT_NSEQ(@"UpdatedItem", [button title]); EXPECT_EQ(std::string("|Item 0,Item 1,UpdatedItem|"), GetViewContent()); @@ -456,9 +456,48 @@ TEST_F(AppsGridControllerTest, ModelUpdates) { // Icon should always be resized to 48x48. EXPECT_EQ(kTargetImageSize, icon_size.width); EXPECT_EQ(kTargetImageSize, icon_size.height); +} + +TEST_F(AppsGridControllerTest, ModelAdd) { + model()->PopulateApps(2); + EXPECT_EQ(2u, [[GetPageAt(0) content] count]); + EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent()); + + app_list::AppListItemList* item_list = model()->item_list(); + + model()->CreateAndAddItem("Item 2"); + ASSERT_EQ(3u, item_list->item_count()); + EXPECT_EQ(3u, [apps_grid_controller_ itemCount]); + EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent()); + + // Test adding an item whose position is in the middle. + app_list::AppListItemModel* item0 = item_list->item_at(0); + app_list::AppListItemModel* item1 = item_list->item_at(1); + app_list::AppListItemModel* item3 = + model()->CreateItem("Item Three", "Item Three"); + item3->set_position(item0->position().CreateBetween(item1->position())); + item_list->AddItem(item3); + EXPECT_EQ(4u, [apps_grid_controller_ itemCount]); + EXPECT_EQ(std::string("|Item 0,Item Three,Item 1,Item 2|"), GetViewContent()); +} + +TEST_F(AppsGridControllerTest, ModelMove) { + model()->PopulateApps(3); + EXPECT_EQ(3u, [[GetPageAt(0) content] count]); + EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent()); + + // Test swapping items (e.g. rearranging via sync). + model()->item_list()->MoveItem(1, 2); + EXPECT_EQ(std::string("|Item 0,Item 2,Item 1|"), GetViewContent()); +} + +TEST_F(AppsGridControllerTest, ModelRemove) { + model()->PopulateApps(3); + EXPECT_EQ(3u, [[GetPageAt(0) content] count]); + EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent()); // Test removing an item at the end. - model()->apps()->DeleteAt(2); + model()->item_list()->DeleteItem("Item 2"); EXPECT_EQ(2u, [apps_grid_controller_ itemCount]); EXPECT_EQ(std::string("|Item 0,Item 1|"), GetViewContent()); @@ -466,29 +505,34 @@ TEST_F(AppsGridControllerTest, ModelUpdates) { model()->CreateAndAddItem("Item 2"); EXPECT_EQ(3u, [apps_grid_controller_ itemCount]); EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent()); - model()->apps()->DeleteAt(1); + model()->item_list()->DeleteItem("Item 1"); EXPECT_EQ(2u, [apps_grid_controller_ itemCount]); EXPECT_EQ(std::string("|Item 0,Item 2|"), GetViewContent()); +} - // Test inserting in the middle. - model()->apps()->AddAt(1, model()->CreateItem("Item One", "Item One")); - EXPECT_EQ(3u, [apps_grid_controller_ itemCount]); - EXPECT_EQ(std::string("|Item 0,Item One,Item 2|"), GetViewContent()); - - // Test swapping items (e.g. rearranging via sync). - model()->apps()->Move(1, 2); - EXPECT_EQ(std::string("|Item 0,Item 2,Item One|"), GetViewContent()); +TEST_F(AppsGridControllerTest, ModelRemoveAlll) { + model()->PopulateApps(3); + EXPECT_EQ(3u, [[GetPageAt(0) content] count]); + EXPECT_EQ(std::string("|Item 0,Item 1,Item 2|"), GetViewContent()); // Test removing multiple items via the model. - model()->apps()->DeleteAll(); + model()->item_list()->DeleteItemsByType(NULL /* all items */); EXPECT_EQ(0u, [apps_grid_controller_ itemCount]); EXPECT_EQ(std::string("||"), GetViewContent()); +} - // Test removing the last item when there is one item on the second page. - ReplaceTestModel(kItemsPerPage + 1); +TEST_F(AppsGridControllerTest, ModelRemovePage) { + app_list::AppListItemList* item_list = model()->item_list(); + + model()->PopulateApps(kItemsPerPage + 1); + ASSERT_EQ(kItemsPerPage + 1, item_list->item_count()); EXPECT_EQ(kItemsPerPage + 1, [apps_grid_controller_ itemCount]); EXPECT_EQ(2u, [apps_grid_controller_ pageCount]); - model()->apps()->DeleteAt(kItemsPerPage); + + // Test removing the last item when there is one item on the second page. + app_list::AppListItemModel* last_item = item_list->item_at(kItemsPerPage); + item_list->DeleteItem(last_item->id()); + EXPECT_EQ(kItemsPerPage, item_list->item_count()); EXPECT_EQ(kItemsPerPage, [apps_grid_controller_ itemCount]); EXPECT_EQ(1u, [apps_grid_controller_ pageCount]); } @@ -499,7 +543,7 @@ TEST_F(AppsGridControllerTest, ItemInstallProgress) { EXPECT_EQ(2u, [apps_grid_controller_ pageCount]); EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]); app_list::AppListItemModel* item_model = - model()->apps()->GetItemAt(kItemsPerPage); + model()->item_list()->item_at(kItemsPerPage); // Highlighting an item should activate the page it is on. item_model->SetHighlighted(true); @@ -535,7 +579,7 @@ TEST_F(AppsGridControllerTest, ItemInstallProgress) { // Two things can be installing simultaneously. When one starts or completes // the model builder will ask for the item to be highlighted. app_list::AppListItemModel* alternate_item_model = - model()->apps()->GetItemAt(0); + model()->item_list()->item_at(0); item_model->SetHighlighted(false); alternate_item_model->SetHighlighted(true); EXPECT_EQ(0u, [apps_grid_controller_ visiblePage]); @@ -906,8 +950,8 @@ TEST_F(AppsGridControllerTest, ScrollingWhileDragging) { TEST_F(AppsGridControllerTest, ContextMenus) { AppListItemWithMenu* item_two_model = new AppListItemWithMenu("Item Two"); - model()->apps()->AddAt(0, new AppListItemWithMenu("Item One")); - model()->apps()->AddAt(1, item_two_model); + model()->item_list()->AddItem(new AppListItemWithMenu("Item One")); + model()->item_list()->AddItem(item_two_model); EXPECT_EQ(2u, [apps_grid_controller_ itemCount]); NSCollectionView* page = [apps_grid_controller_ collectionViewAtPageIndex:0]; diff --git a/ui/app_list/cocoa/apps_grid_view_item.mm b/ui/app_list/cocoa/apps_grid_view_item.mm index b6e4fb5..ccd6b80 100644 --- a/ui/app_list/cocoa/apps_grid_view_item.mm +++ b/ui/app_list/cocoa/apps_grid_view_item.mm @@ -80,7 +80,7 @@ class ItemModelObserverBridge : public app_list::AppListItemModelObserver { private: AppsGridViewItem* parent_; // Weak. Owns us. - AppListItemModel* model_; // Weak. Owned by AppListModel::Apps. + AppListItemModel* model_; // Weak. Owned by AppListModel. base::scoped_nsobject<MenuController> context_menu_controller_; DISALLOW_COPY_AND_ASSIGN(ItemModelObserverBridge); diff --git a/ui/app_list/test/app_list_test_model.cc b/ui/app_list/test/app_list_test_model.cc index 6f6bf10..4fd1acc 100644 --- a/ui/app_list/test/app_list_test_model.cc +++ b/ui/app_list/test/app_list_test_model.cc @@ -10,6 +10,9 @@ namespace app_list { namespace test { +// static +const char AppListTestModel::kAppType[] = "FolderItem"; + class AppListTestModel::AppListTestItemModel : public AppListItemModel { public: AppListTestItemModel(const std::string& id, AppListTestModel* model) @@ -22,6 +25,10 @@ class AppListTestModel::AppListTestItemModel : public AppListItemModel { model_->ItemActivated(this); } + virtual const char* GetAppType() const OVERRIDE { + return AppListTestModel::kAppType; + } + private: AppListTestModel* model_; DISALLOW_COPY_AND_ASSIGN(AppListTestItemModel); @@ -38,7 +45,7 @@ std::string AppListTestModel::GetItemName(int id) { } void AppListTestModel::PopulateApps(int n) { - int start_index = apps()->item_count(); + int start_index = item_list()->item_count(); for (int i = 0; i < n; ++i) CreateAndAddItem(GetItemName(start_index + i)); } @@ -49,10 +56,10 @@ void AppListTestModel::PopulateAppWithId(int id) { std::string AppListTestModel::GetModelContent() { std::string content; - for (size_t i = 0; i < apps()->item_count(); ++i) { + for (size_t i = 0; i < item_list()->item_count(); ++i) { if (i > 0) content += ','; - content += apps()->GetItemAt(i)->title(); + content += item_list()->item_at(i)->title(); } return content; } @@ -60,13 +67,20 @@ std::string AppListTestModel::GetModelContent() { AppListItemModel* AppListTestModel::CreateItem(const std::string& title, const std::string& full_name) { AppListItemModel* item = new AppListTestItemModel(title, this); + size_t nitems = item_list()->item_count(); + syncer::StringOrdinal position; + if (nitems == 0) + position = syncer::StringOrdinal::CreateInitialOrdinal(); + else + position = item_list()->item_at(nitems - 1)->position().CreateAfter(); + item->set_position(position); item->SetTitleAndFullName(title, full_name); return item; } void AppListTestModel::CreateAndAddItem(const std::string& title, const std::string& full_name) { - AddItem(CreateItem(title, full_name)); + item_list()->AddItem(CreateItem(title, full_name)); } void AppListTestModel::CreateAndAddItem(const std::string& title) { @@ -74,7 +88,7 @@ void AppListTestModel::CreateAndAddItem(const std::string& title) { } void AppListTestModel::HighlightItemAt(int index) { - AppListItemModel* item = apps()->GetItemAt(index); + AppListItemModel* item = item_list()->item_at(index); item->SetHighlighted(true); } diff --git a/ui/app_list/test/app_list_test_model.h b/ui/app_list/test/app_list_test_model.h index 548072e..473152e 100644 --- a/ui/app_list/test/app_list_test_model.h +++ b/ui/app_list/test/app_list_test_model.h @@ -48,6 +48,8 @@ class AppListTestModel : public AppListModel { int activate_count() { return activate_count_; } AppListItemModel* last_activated() { return last_activated_; } + static const char kAppType[]; + private: class AppListTestItemModel; diff --git a/ui/app_list/views/app_list_folder_view.cc b/ui/app_list/views/app_list_folder_view.cc index ec6e24f..44d31b8 100644 --- a/ui/app_list/views/app_list_folder_view.cc +++ b/ui/app_list/views/app_list_folder_view.cc @@ -55,7 +55,7 @@ AppListFolderView::~AppListFolderView() { void AppListFolderView::SetAppListFolderItem(AppListFolderItem* folder) { folder_item_ = folder; - items_grid_view_->SetApps(folder_item_->apps()); + items_grid_view_->SetItemList(folder_item_->item_list()); folder_header_view_->SetFolderItem(folder_item_); } @@ -93,4 +93,3 @@ void AppListFolderView::NavigateBack(AppListFolderItem* item, } } // namespace app_list - diff --git a/ui/app_list/views/app_list_item_view.h b/ui/app_list/views/app_list_item_view.h index 311d721..08fe244 100644 --- a/ui/app_list/views/app_list_item_view.h +++ b/ui/app_list/views/app_list_item_view.h @@ -105,7 +105,7 @@ class APP_LIST_EXPORT AppListItemView : public views::CustomButton, // ui::EventHandler overrides: virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE; - AppListItemModel* model_; // Owned by AppListModel::Apps. + AppListItemModel* model_; // Owned by AppListModel. AppsGridView* apps_grid_view_; // Owned by views hierarchy. views::ImageView* icon_; // Owned by views hierarchy. diff --git a/ui/app_list/views/app_list_main_view.cc b/ui/app_list/views/app_list_main_view.cc index 0a83325..dd1a30c 100644 --- a/ui/app_list/views/app_list_main_view.cc +++ b/ui/app_list/views/app_list_main_view.cc @@ -161,12 +161,12 @@ void AppListMainView::PreloadIcons(PaginationModel* pagination_model, 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()), + static_cast<int>(model_->item_list()->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); + AppListItemModel* item = model_->item_list()->item_at(i); if (item->icon().HasRepresentation(scale)) continue; diff --git a/ui/app_list/views/apps_container_view.cc b/ui/app_list/views/apps_container_view.cc index ea520b6..6becc49 100644 --- a/ui/app_list/views/apps_container_view.cc +++ b/ui/app_list/views/apps_container_view.cc @@ -34,7 +34,7 @@ AppsContainerView::AppsContainerView(AppListMainView* app_list_main_view, AddChildView(app_list_folder_view_); apps_grid_view_->SetModel(model_); - apps_grid_view_->SetApps(model_->apps()); + apps_grid_view_->SetItemList(model_->item_list()); } AppsContainerView::~AppsContainerView() { @@ -88,6 +88,3 @@ void AppsContainerView::SetShowState(ShowState show_state) { } } // namespace app_list - - - diff --git a/ui/app_list/views/apps_grid_view.cc b/ui/app_list/views/apps_grid_view.cc index 09d056f..d8f92c3 100644 --- a/ui/app_list/views/apps_grid_view.cc +++ b/ui/app_list/views/apps_grid_view.cc @@ -234,7 +234,7 @@ AppsGridView::AppsGridView(AppsGridViewDelegate* delegate, PaginationModel* pagination_model, content::WebContents* start_page_contents) : model_(NULL), - apps_(NULL), + item_list_(NULL), delegate_(delegate), pagination_model_(pagination_model), page_switcher_view_(new PageSwitcher(pagination_model)), @@ -273,8 +273,8 @@ AppsGridView::~AppsGridView() { model_->RemoveObserver(this); pagination_model_->RemoveObserver(this); - if (apps_) - apps_->RemoveObserver(this); + if (item_list_) + item_list_->RemoveObserver(this); } void AppsGridView::SetLayout(int icon_size, int cols, int rows_per_page) { @@ -299,12 +299,12 @@ void AppsGridView::SetModel(AppListModel* model) { Update(); } -void AppsGridView::SetApps(AppListModel::Apps* apps) { - if (apps_) - apps_->RemoveObserver(this); +void AppsGridView::SetItemList(AppListItemList* item_list) { + if (item_list_) + item_list_->RemoveObserver(this); - apps_ = apps; - apps_->AddObserver(this); + item_list_ = item_list; + item_list_->AddObserver(this); Update(); } @@ -631,12 +631,21 @@ void AppsGridView::ViewHierarchyChanged( void AppsGridView::Update() { DCHECK(!selected_view_ && !drag_view_); - if (!apps_) + if (!item_list_) return; view_model_.Clear(); - if (apps_ && apps_->item_count()) - ListItemsAdded(0, apps_->item_count()); + if (!item_list_->item_count()) + return; + for (size_t i = 0; i < item_list_->item_count(); ++i) { + views::View* view = CreateViewForItemAtIndex(i); + view_model_.Add(view, i); + AddChildView(view); + } + UpdatePaging(); + UpdatePulsingBlockViews(); + Layout(); + SchedulePaint(); } void AppsGridView::UpdatePaging() { @@ -649,7 +658,7 @@ void AppsGridView::UpdatePaging() { void AppsGridView::UpdatePulsingBlockViews() { const int available_slots = - tiles_per_page() - apps_->item_count() % tiles_per_page(); + tiles_per_page() - item_list_->item_count() % tiles_per_page(); const int desired = model_->status() == AppListModel::STATUS_SYNCING ? available_slots : 0; @@ -671,9 +680,9 @@ void AppsGridView::UpdatePulsingBlockViews() { } views::View* AppsGridView::CreateViewForItemAtIndex(size_t index) { - DCHECK_LT(index, apps_->item_count()); + DCHECK_LT(index, item_list_->item_count()); AppListItemView* view = new AppListItemView(this, - apps_->GetItemAt(index)); + item_list_->item_at(index)); view->SetIconSize(icon_size_); #if defined(USE_AURA) view->SetPaintToLayer(true); @@ -1108,10 +1117,10 @@ void AppsGridView::MoveItemInModel(views::View* item_view, if (target_model_index == current_model_index) return; - apps_->RemoveObserver(this); - apps_->Move(current_model_index, target_model_index); + item_list_->RemoveObserver(this); + item_list_->MoveItem(current_model_index, target_model_index); view_model_.Move(current_model_index, target_model_index); - apps_->AddObserver(this); + item_list_->AddObserver(this); if (pagination_model_->selected_page() != target.page) pagination_model_->SelectPage(target.page, false); @@ -1170,14 +1179,12 @@ void AppsGridView::LayoutStartPage() { start_page_view_->SetBoundsRect(start_page_bounds); } -void AppsGridView::ListItemsAdded(size_t start, size_t count) { +void AppsGridView::OnListItemAdded(size_t index, AppListItemModel* item) { EndDrag(true); - for (size_t i = start; i < start + count; ++i) { - views::View* view = CreateViewForItemAtIndex(i); - view_model_.Add(view, i); - AddChildView(view); - } + views::View* view = CreateViewForItemAtIndex(index); + view_model_.Add(view, index); + AddChildView(view); UpdatePaging(); UpdatePulsingBlockViews(); @@ -1185,14 +1192,12 @@ void AppsGridView::ListItemsAdded(size_t start, size_t count) { SchedulePaint(); } -void AppsGridView::ListItemsRemoved(size_t start, size_t count) { +void AppsGridView::OnListItemRemoved(size_t index, AppListItemModel* item) { EndDrag(true); - for (size_t i = 0; i < count; ++i) { - views::View* view = view_model_.view_at(start); - view_model_.Remove(start); - delete view; - } + views::View* view = view_model_.view_at(index); + view_model_.Remove(index); + delete view; UpdatePaging(); UpdatePulsingBlockViews(); @@ -1200,18 +1205,16 @@ void AppsGridView::ListItemsRemoved(size_t start, size_t count) { SchedulePaint(); } -void AppsGridView::ListItemMoved(size_t index, size_t target_index) { +void AppsGridView::OnListItemMoved(size_t from_index, + size_t to_index, + AppListItemModel* item) { EndDrag(true); - view_model_.Move(index, target_index); + view_model_.Move(from_index, to_index); UpdatePaging(); AnimateToIdealBounds(); } -void AppsGridView::ListItemsChanged(size_t start, size_t count) { - NOTREACHED(); -} - void AppsGridView::TotalPagesChanged() { } diff --git a/ui/app_list/views/apps_grid_view.h b/ui/app_list/views/apps_grid_view.h index 31f2fed..ec66746 100644 --- a/ui/app_list/views/apps_grid_view.h +++ b/ui/app_list/views/apps_grid_view.h @@ -48,10 +48,10 @@ class AppsGridViewDelegate; class PageSwitcher; class PaginationModel; -// AppsGridView displays a grid for AppListModel::Apps sub model. +// AppsGridView displays a grid for AppListItemList sub model. class APP_LIST_EXPORT AppsGridView : public views::View, public views::ButtonListener, - public ui::ListModelObserver, + public AppListItemListObserver, public PaginationModelObserver, public AppListModelObserver { public: @@ -78,8 +78,9 @@ class APP_LIST_EXPORT AppsGridView : public views::View, // Sets |model| to use. Note this does not take ownership of |model|. void SetModel(AppListModel* model); - // Set |apps| to renders. Note this does not take ownership of |apps|. - void SetApps(AppListModel::Apps* apps); + // Sets the |item_list| to render. Note this does not take ownership of + // |item_list|. + void SetItemList(AppListItemList* item_list); void SetSelectedView(views::View* view); void ClearSelectedView(views::View* view); @@ -250,11 +251,12 @@ class APP_LIST_EXPORT AppsGridView : public views::View, virtual void ButtonPressed(views::Button* sender, const ui::Event& event) OVERRIDE; - // Overridden from ListModelObserver: - virtual void ListItemsAdded(size_t start, size_t count) OVERRIDE; - virtual void ListItemsRemoved(size_t start, size_t count) OVERRIDE; - virtual void ListItemMoved(size_t index, size_t target_index) OVERRIDE; - virtual void ListItemsChanged(size_t start, size_t count) OVERRIDE; + // Overridden from AppListItemListObserver: + virtual void OnListItemAdded(size_t index, AppListItemModel* item) OVERRIDE; + virtual void OnListItemRemoved(size_t index, AppListItemModel* item) OVERRIDE; + virtual void OnListItemMoved(size_t from_index, + size_t to_index, + AppListItemModel* item) OVERRIDE; // Overridden from PaginationModelObserver: virtual void TotalPagesChanged() OVERRIDE; @@ -272,7 +274,7 @@ class APP_LIST_EXPORT AppsGridView : public views::View, void SetViewHidden(views::View* view, bool hide, bool immediate); AppListModel* model_; // Owned by AppListView. - AppListModel::Apps* apps_; // Not owned. + AppListItemList* item_list_; // Not owned. AppsGridViewDelegate* delegate_; PaginationModel* pagination_model_; // Owned by AppListController. PageSwitcher* page_switcher_view_; // Owned by views hierarchy. diff --git a/ui/app_list/views/apps_grid_view_unittest.cc b/ui/app_list/views/apps_grid_view_unittest.cc index 896f076..34c0c77 100644 --- a/ui/app_list/views/apps_grid_view_unittest.cc +++ b/ui/app_list/views/apps_grid_view_unittest.cc @@ -108,7 +108,7 @@ class AppsGridViewTest : public views::ViewsTestBase { apps_grid_view_->SetLayout(kIconDimension, kCols, kRows); apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight))); apps_grid_view_->SetModel(model_.get()); - apps_grid_view_->SetApps(model_->apps()); + apps_grid_view_->SetItemList(model_->item_list()); test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get())); } @@ -124,7 +124,7 @@ class AppsGridViewTest : public views::ViewsTestBase { } AppListItemView* GetItemViewForPoint(const gfx::Point& point) { - for (size_t i = 0; i < model_->apps()->item_count(); ++i) { + for (size_t i = 0; i < model_->item_list()->item_count(); ++i) { AppListItemView* view = GetItemViewAt(i); if (view->bounds().Contains(point)) return view; @@ -133,7 +133,7 @@ class AppsGridViewTest : public views::ViewsTestBase { } gfx::Rect GetItemTileRectAt(int row, int col) { - DCHECK_GT(model_->apps()->item_count(), 0u); + DCHECK_GT(model_->item_list()->item_count(), 0u); gfx::Insets insets(apps_grid_view_->GetInsets()); gfx::Rect rect(gfx::Point(insets.left(), insets.top()), @@ -206,7 +206,7 @@ TEST_F(AppsGridViewTest, EnsureHighlightedVisible) { EXPECT_EQ(1, pagination_model_->selected_page()); // Highlight last one in the model and last page should be selected. - model_->HighlightItemAt(model_->apps()->item_count() - 1); + model_->HighlightItemAt(model_->item_list()->item_count() - 1); EXPECT_EQ(kPages - 1, pagination_model_->selected_page()); } @@ -218,7 +218,7 @@ TEST_F(AppsGridViewTest, RemoveSelectedLastApp) { AppListItemView* last_view = GetItemViewAt(kLastItemIndex); apps_grid_view_->SetSelectedView(last_view); - model_->apps()->DeleteAt(kLastItemIndex); + model_->item_list()->DeleteItem(model_->GetItemName(kLastItemIndex)); EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view)); @@ -253,7 +253,7 @@ TEST_F(AppsGridViewTest, MouseDrag) { // Deleting an item keeps remaining intact. SimulateDrag(AppsGridView::MOUSE, from, to); - model_->apps()->DeleteAt(1); + model_->item_list()->DeleteItem(model_->GetItemName(0)); apps_grid_view_->EndDrag(false); EXPECT_EQ(std::string("Item 1,Item 2,Item 3"), model_->GetModelContent()); |