diff options
-rw-r--r-- | chrome/browser/ui/app_list/app_list_view_delegate.cc | 10 | ||||
-rw-r--r-- | chrome/browser/ui/app_list/app_list_view_delegate.h | 8 | ||||
-rw-r--r-- | chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.cc | 30 | ||||
-rw-r--r-- | chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h | 36 | ||||
-rw-r--r-- | chrome/chrome_browser_ui.gypi | 2 | ||||
-rw-r--r-- | ui/app_list/app_list.gyp | 3 | ||||
-rw-r--r-- | ui/app_list/app_list_model.cc | 22 | ||||
-rw-r--r-- | ui/app_list/app_list_model.h | 16 | ||||
-rw-r--r-- | ui/app_list/app_list_model_observer.h | 23 | ||||
-rw-r--r-- | ui/app_list/apps_grid_view.cc | 76 | ||||
-rw-r--r-- | ui/app_list/apps_grid_view.h | 18 | ||||
-rw-r--r-- | ui/app_list/apps_grid_view_unittest.cc | 34 | ||||
-rw-r--r-- | ui/app_list/contents_view.cc | 2 | ||||
-rw-r--r-- | ui/app_list/pulsing_block_view.cc | 106 | ||||
-rw-r--r-- | ui/app_list/pulsing_block_view.h | 40 |
15 files changed, 390 insertions, 36 deletions
diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.cc b/chrome/browser/ui/app_list/app_list_view_delegate.cc index 56ff598..efdc66f 100644 --- a/chrome/browser/ui/app_list/app_list_view_delegate.cc +++ b/chrome/browser/ui/app_list/app_list_view_delegate.cc @@ -10,6 +10,10 @@ #include "chrome/browser/ui/app_list/search_builder.h" #include "content/public/browser/user_metrics.h" +#if defined(USE_ASH) +#include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h" +#endif + AppListViewDelegate::AppListViewDelegate(AppListControllerDelegate* controller) : controller_(controller) {} @@ -27,9 +31,15 @@ void AppListViewDelegate::SetModel(app_list::AppListModel* model) { model->search_box(), model->results(), controller_.get())); +#if defined(USE_ASH) + app_sync_ui_state_watcher_.reset(new AppSyncUIStateWatcher(profile, model)); +#endif } else { apps_builder_.reset(); search_builder_.reset(); +#if defined(USE_ASH) + app_sync_ui_state_watcher_.reset(); +#endif } } diff --git a/chrome/browser/ui/app_list/app_list_view_delegate.h b/chrome/browser/ui/app_list/app_list_view_delegate.h index c7bbae5..1159e9e 100644 --- a/chrome/browser/ui/app_list/app_list_view_delegate.h +++ b/chrome/browser/ui/app_list/app_list_view_delegate.h @@ -16,6 +16,10 @@ class AppsModelBuilder; class SearchBuilder; +#if defined(USE_ASH) +class AppSyncUIStateWatcher; +#endif + class AppListViewDelegate : public app_list::AppListViewDelegate { public: // The delegate will take ownership of the controller. @@ -42,6 +46,10 @@ class AppListViewDelegate : public app_list::AppListViewDelegate { scoped_ptr<SearchBuilder> search_builder_; scoped_ptr<AppListControllerDelegate> controller_; +#if defined(USE_ASH) + scoped_ptr<AppSyncUIStateWatcher> app_sync_ui_state_watcher_; +#endif + DISALLOW_COPY_AND_ASSIGN(AppListViewDelegate); }; diff --git a/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.cc b/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.cc new file mode 100644 index 0000000..c791c00 --- /dev/null +++ b/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.cc @@ -0,0 +1,30 @@ +// Copyright (c) 2012 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 "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h" + +#include "chrome/browser/ui/ash/app_sync_ui_state.h" +#include "ui/app_list/app_list_model.h" + +AppSyncUIStateWatcher::AppSyncUIStateWatcher(Profile* profile, + app_list::AppListModel* model) + : app_sync_ui_state_(AppSyncUIState::Get(profile)), + model_(model) { + if (app_sync_ui_state_) { + app_sync_ui_state_->AddObserver(this); + OnAppSyncUIStatusChanged(); + } +} + +AppSyncUIStateWatcher::~AppSyncUIStateWatcher() { + if (app_sync_ui_state_) + app_sync_ui_state_->RemoveObserver(this); +} + +void AppSyncUIStateWatcher::OnAppSyncUIStatusChanged() { + if (app_sync_ui_state_->status() == AppSyncUIState::STATUS_SYNCING) + model_->SetStatus(app_list::AppListModel::STATUS_SYNCING); + else + model_->SetStatus(app_list::AppListModel::STATUS_NORMAL); +} diff --git a/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h b/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h new file mode 100644 index 0000000..5ec74a1 --- /dev/null +++ b/chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h @@ -0,0 +1,36 @@ +// Copyright (c) 2012 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 CHROME_BROWSER_UI_ASH_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_ +#define CHROME_BROWSER_UI_ASH_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/browser/ui/ash/app_sync_ui_state_observer.h" + +namespace app_list { +class AppListModel; +} + +class AppSyncUIState; +class Profile; + +// AppSyncUIStateWatcher updates AppListModel status when AppSyncUIState +// of the given profile changes. +class AppSyncUIStateWatcher : public AppSyncUIStateObserver { + public: + AppSyncUIStateWatcher(Profile* profile, app_list::AppListModel* model); + virtual ~AppSyncUIStateWatcher(); + + private: + // AppSyncUIStateObserver overrides: + virtual void OnAppSyncUIStatusChanged() OVERRIDE; + + AppSyncUIState* app_sync_ui_state_; + app_list::AppListModel* model_; // Owned by AppListView + + DISALLOW_COPY_AND_ASSIGN(AppSyncUIStateWatcher); +}; + +#endif // CHROME_BROWSER_UI_ASH_APP_LIST_APP_SYNC_UI_STATE_WATCHER_H_ diff --git a/chrome/chrome_browser_ui.gypi b/chrome/chrome_browser_ui.gypi index 8c6f424..d5097d4 100644 --- a/chrome/chrome_browser_ui.gypi +++ b/chrome/chrome_browser_ui.gypi @@ -109,6 +109,8 @@ 'browser/ui/ash/ash_init.h', 'browser/ui/ash/app_list/app_list_controller_ash.h', 'browser/ui/ash/app_list/app_list_controller_ash.cc', + 'browser/ui/ash/app_list/app_sync_ui_state_watcher.cc', + 'browser/ui/ash/app_list/app_sync_ui_state_watcher.h', 'browser/ui/ash/app_sync_ui_state.cc', 'browser/ui/ash/app_sync_ui_state.h', 'browser/ui/ash/app_sync_ui_state_observer.h', diff --git a/ui/app_list/app_list.gyp b/ui/app_list/app_list.gyp index 5043550..c40236c 100644 --- a/ui/app_list/app_list.gyp +++ b/ui/app_list/app_list.gyp @@ -34,6 +34,7 @@ 'app_list_item_view.h', 'app_list_model.cc', 'app_list_model.h', + 'app_list_model_observer.h', 'app_list_switches.cc', 'app_list_switches.h', 'app_list_view.cc', @@ -49,6 +50,8 @@ 'pagination_model.cc', 'pagination_model.h', 'pagination_model_observer.h', + 'pulsing_block_view.cc', + 'pulsing_block_view.h', 'search_box_model.h', 'search_box_model.cc', 'search_box_model_observer.h', diff --git a/ui/app_list/app_list_model.cc b/ui/app_list/app_list_model.cc index a4a8193..4f473ef 100644 --- a/ui/app_list/app_list_model.cc +++ b/ui/app_list/app_list_model.cc @@ -5,6 +5,7 @@ #include "ui/app_list/app_list_model.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" @@ -13,10 +14,29 @@ namespace app_list { AppListModel::AppListModel() : apps_(new Apps), search_box_(new SearchBoxModel), - results_(new SearchResults) { + results_(new SearchResults), + status_(STATUS_NORMAL) { } AppListModel::~AppListModel() { } +void AppListModel::AddObserver(AppListModelObserver* observer) { + observers_.AddObserver(observer); +} + +void AppListModel::RemoveObserver(AppListModelObserver* observer) { + observers_.RemoveObserver(observer); +} + +void AppListModel::SetStatus(Status status) { + if (status_ == status) + return; + + status_ = status; + FOR_EACH_OBSERVER(AppListModelObserver, + observers_, + OnAppListModelStatusChanged()); +} + } // namespace app_list diff --git a/ui/app_list/app_list_model.h b/ui/app_list/app_list_model.h index d0c5923..4c46a01 100644 --- a/ui/app_list/app_list_model.h +++ b/ui/app_list/app_list_model.h @@ -7,12 +7,14 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "base/observer_list.h" #include "ui/app_list/app_list_export.h" #include "ui/base/models/list_model.h" namespace app_list { class AppListItemModel; +class AppListModelObserver; class SearchBoxModel; class SearchResult; @@ -22,15 +24,26 @@ class SearchResult; // the model for SearchBoxView. SearchResults owns a list of SearchResult. class APP_LIST_EXPORT AppListModel { public: + enum Status { + STATUS_NORMAL, + STATUS_SYNCING, // Syncing apps or installing synced apps. + }; + typedef ui::ListModel<AppListItemModel> Apps; typedef ui::ListModel<SearchResult> SearchResults; AppListModel(); ~AppListModel(); + void AddObserver(AppListModelObserver* observer); + void RemoveObserver(AppListModelObserver* observer); + + void SetStatus(Status status); + Apps* apps() { return apps_.get(); } SearchBoxModel* search_box() { return search_box_.get(); } SearchResults* results() { return results_.get(); } + Status status() const { return status_; } private: scoped_ptr<Apps> apps_; @@ -38,6 +51,9 @@ class APP_LIST_EXPORT AppListModel { scoped_ptr<SearchBoxModel> search_box_; scoped_ptr<SearchResults> results_; + Status status_; + ObserverList<AppListModelObserver> observers_; + DISALLOW_COPY_AND_ASSIGN(AppListModel); }; diff --git a/ui/app_list/app_list_model_observer.h b/ui/app_list/app_list_model_observer.h new file mode 100644 index 0000000..c843894 --- /dev/null +++ b/ui/app_list/app_list_model_observer.h @@ -0,0 +1,23 @@ +// Copyright (c) 2012 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_MODEL_OBSERVER_H_ +#define UI_APP_LIST_APP_LIST_MODEL_OBSERVER_H_ + +#include "ui/app_list/app_list_export.h" + +namespace app_list { + +class APP_LIST_EXPORT AppListModelObserver { + public: + // Invoked when AppListModel's status has changed. + virtual void OnAppListModelStatusChanged() = 0; + + protected: + virtual ~AppListModelObserver() {} +}; + +} // namespace app_list + +#endif // UI_APP_LIST_APP_LIST_MODEL_OBSERVER_H_ diff --git a/ui/app_list/apps_grid_view.cc b/ui/app_list/apps_grid_view.cc index 08ab470..666fef1 100644 --- a/ui/app_list/apps_grid_view.cc +++ b/ui/app_list/apps_grid_view.cc @@ -11,6 +11,7 @@ #include "ui/app_list/apps_grid_view_delegate.h" #include "ui/app_list/page_switcher.h" #include "ui/app_list/pagination_model.h" +#include "ui/app_list/pulsing_block_view.h" #include "ui/base/animation/animation.h" #include "ui/base/events/event.h" #include "ui/views/border.h" @@ -109,8 +110,10 @@ AppsGridView::AppsGridView(AppsGridViewDelegate* delegate, } AppsGridView::~AppsGridView() { - if (model_) + if (model_) { model_->RemoveObserver(this); + model_->apps()->RemoveObserver(this); + } pagination_model_->RemoveObserver(this); } @@ -125,13 +128,17 @@ void AppsGridView::SetLayout(int icon_size, int cols, int rows_per_page) { kLeftRightPadding)); } -void AppsGridView::SetModel(AppListModel::Apps* model) { - if (model_) +void AppsGridView::SetModel(AppListModel* model) { + if (model_) { model_->RemoveObserver(this); + model_->apps()->RemoveObserver(this); + } model_ = model; - if (model_) + if (model_) { model_->AddObserver(this); + model_->apps()->AddObserver(this); + } Update(); } @@ -165,7 +172,7 @@ void AppsGridView::EnsureViewVisible(const views::View* view) { void AppsGridView::InitiateDrag(views::View* view, Pointer pointer, const ui::LocatedEvent& event) { - if (drag_view_) + if (drag_view_ || pulsing_blocks_model_.view_size()) return; drag_view_ = view; @@ -243,6 +250,7 @@ void AppsGridView::Layout() { if (view != drag_view_) view->SetBoundsRect(view_model_.ideal_bounds(i)); } + views::ViewModelUtils::SetViewBoundsToIdealBounds(pulsing_blocks_model_); const int page_switcher_height = page_switcher_view_->GetPreferredSize().height(); @@ -312,8 +320,8 @@ void AppsGridView::ViewHierarchyChanged(bool is_add, void AppsGridView::Update() { selected_view_ = NULL; view_model_.Clear(); - if (model_ && model_->item_count()) - ListItemsAdded(0, model_->item_count()); + if (model_ && model_->apps()->item_count()) + ListItemsAdded(0, model_->apps()->item_count()); } void AppsGridView::UpdatePaging() { @@ -326,10 +334,33 @@ void AppsGridView::UpdatePaging() { (view_model_.view_size() - 1) / tiles_per_page() + 1); } +void AppsGridView::UpdatePulsingBlockViews() { + const int available_slots = + tiles_per_page() - model_->apps()->item_count() % tiles_per_page(); + const int desired = model_->status() == AppListModel::STATUS_SYNCING ? + available_slots : 0; + + if (pulsing_blocks_model_.view_size() == desired) + return; + + while (pulsing_blocks_model_.view_size() > desired) { + views::View* view = pulsing_blocks_model_.view_at(0); + pulsing_blocks_model_.Remove(0); + delete view; + } + + while (pulsing_blocks_model_.view_size() < desired) { + views::View* view = new PulsingBlockView( + gfx::Size(kPreferredTileWidth, kPreferredTileHeight), true); + pulsing_blocks_model_.Add(view, 0); + AddChildView(view); + } +} + views::View* AppsGridView::CreateViewForItemAtIndex(size_t index) { - DCHECK_LT(index, model_->item_count()); + DCHECK_LT(index, model_->apps()->item_count()); AppListItemView* view = new AppListItemView(this, - model_->GetItemAt(index)); + model_->apps()->GetItemAt(index)); view->SetIconSize(icon_size_); #if !defined(OS_WIN) view->SetPaintToLayer(true); @@ -424,9 +455,11 @@ void AppsGridView::CalculateIdealBounds() { const int transition_offset = is_valid ? transition.progress * page_width * dir : 0; + const int total_views = + view_model_.view_size() + pulsing_blocks_model_.view_size(); int slot_index = 0; - for (int i = 0; i < view_model_.view_size(); ++i) { - if (view_model_.view_at(i) == drag_view_) + for (int i = 0; i < total_views; ++i) { + if (i < view_model_.view_size() && view_model_.view_at(i) == drag_view_) continue; int page = slot_index / tiles_per_page(); @@ -456,7 +489,12 @@ void AppsGridView::CalculateIdealBounds() { gfx::Point(grid_rect.x() + col * tile_size.width() + x_offset, grid_rect.y() + row * tile_size.height()), tile_size); - view_model_.set_ideal_bounds(i, tile_slot); + if (i < view_model_.view_size()) { + view_model_.set_ideal_bounds(i, tile_slot); + } else { + pulsing_blocks_model_.set_ideal_bounds(i - view_model_.view_size(), + tile_slot); + } ++slot_index; } @@ -619,10 +657,10 @@ void AppsGridView::MoveItemInModel(views::View* item_view, if (target_model_index == current_model_index) return; - model_->RemoveObserver(this); - model_->Move(current_model_index, target_model_index); + model_->apps()->RemoveObserver(this); + model_->apps()->Move(current_model_index, target_model_index); view_model_.Move(current_model_index, target_model_index); - model_->AddObserver(this); + model_->apps()->AddObserver(this); if (pagination_model_->selected_page() != target.page) pagination_model_->SelectPage(target.page, false); @@ -652,6 +690,7 @@ void AppsGridView::ListItemsAdded(size_t start, size_t count) { } UpdatePaging(); + UpdatePulsingBlockViews(); Layout(); SchedulePaint(); } @@ -666,6 +705,7 @@ void AppsGridView::ListItemsRemoved(size_t start, size_t count) { } UpdatePaging(); + UpdatePulsingBlockViews(); Layout(); SchedulePaint(); } @@ -704,4 +744,10 @@ void AppsGridView::TransitionChanged() { Layout(); } +void AppsGridView::OnAppListModelStatusChanged() { + UpdatePulsingBlockViews(); + Layout(); + SchedulePaint(); +} + } // namespace app_list diff --git a/ui/app_list/apps_grid_view.h b/ui/app_list/apps_grid_view.h index 873c4d2..868938f 100644 --- a/ui/app_list/apps_grid_view.h +++ b/ui/app_list/apps_grid_view.h @@ -12,6 +12,7 @@ #include "base/timer.h" #include "ui/app_list/app_list_export.h" #include "ui/app_list/app_list_model.h" +#include "ui/app_list/app_list_model_observer.h" #include "ui/app_list/pagination_model_observer.h" #include "ui/base/models/list_model_observer.h" #include "ui/views/animation/bounds_animator.h" @@ -38,7 +39,8 @@ class PaginationModel; class APP_LIST_EXPORT AppsGridView : public views::View, public views::ButtonListener, public ui::ListModelObserver, - public PaginationModelObserver { + public PaginationModelObserver, + public AppListModelObserver { public: enum Pointer { NONE, @@ -55,7 +57,7 @@ class APP_LIST_EXPORT AppsGridView : public views::View, void SetLayout(int icon_size, int cols, int rows_per_page); // Sets |model| to use. Note this does not take ownership of |model|. - void SetModel(AppListModel::Apps* model); + void SetModel(AppListModel* model); void SetSelectedView(views::View* view); void ClearSelectedView(views::View* view); @@ -113,6 +115,10 @@ class APP_LIST_EXPORT AppsGridView : public views::View, // Updates page splits for item views. void UpdatePaging(); + // Updates the number of pulsing block views based on AppListModel status and + // number of apps. + void UpdatePulsingBlockViews(); + views::View* CreateViewForItemAtIndex(size_t index); void SetSelectedItemByIndex(const Index& index); @@ -168,7 +174,10 @@ class APP_LIST_EXPORT AppsGridView : public views::View, virtual void SelectedPageChanged(int old_selected, int new_selected) OVERRIDE; virtual void TransitionChanged() OVERRIDE; - AppListModel::Apps* model_; // Owned by AppListModel. + // Overridden from AppListModelObserver: + virtual void OnAppListModelStatusChanged() OVERRIDE; + + AppListModel* model_; // Owned by AppListView. AppsGridViewDelegate* delegate_; PaginationModel* pagination_model_; // Owned by AppListController. PageSwitcher* page_switcher_view_; // Owned by views hierarchy. @@ -180,6 +189,9 @@ class APP_LIST_EXPORT AppsGridView : public views::View, // Tracks app item views. There is a view per item in |model_|. views::ViewModel view_model_; + // Tracks pulsing block views. + views::ViewModel pulsing_blocks_model_; + views::View* selected_view_; views::View* drag_view_; diff --git a/ui/app_list/apps_grid_view_unittest.cc b/ui/app_list/apps_grid_view_unittest.cc index ccc7f5c..abd3775 100644 --- a/ui/app_list/apps_grid_view_unittest.cc +++ b/ui/app_list/apps_grid_view_unittest.cc @@ -95,13 +95,13 @@ class AppsGridViewTest : public testing::Test { // testing::Test overrides: virtual void SetUp() OVERRIDE { - apps_model_.reset(new AppListModel::Apps); + model_.reset(new AppListModel); pagination_model_.reset(new PaginationModel); apps_grid_view_.reset(new AppsGridView(NULL, pagination_model_.get())); apps_grid_view_->SetLayout(kIconDimension, kCols, kRows); apps_grid_view_->SetBoundsRect(gfx::Rect(gfx::Size(kWidth, kHeight))); - apps_grid_view_->SetModel(apps_model_.get()); + apps_grid_view_->SetModel(model_.get()); test_api_.reset(new AppsGridViewTestApi(apps_grid_view_.get())); } @@ -113,17 +113,17 @@ class AppsGridViewTest : public testing::Test { void PopulateApps(int n) { for (int i = 0; i < n; ++i) { std::string title = base::StringPrintf("Item %d", i); - apps_model_->Add(CreateItem(title)); + model_->apps()->Add(CreateItem(title)); } } // Get a string of all apps in |model| joined with ','. std::string GetModelContent() { std::string content; - for (size_t i = 0; i < apps_model_->item_count(); ++i) { + for (size_t i = 0; i < model_->apps()->item_count(); ++i) { if (i > 0) content += ','; - content += apps_model_->GetItemAt(i)->title(); + content += model_->apps()->GetItemAt(i)->title(); } return content; } @@ -135,7 +135,7 @@ class AppsGridViewTest : public testing::Test { } void HighlightItemAt(int index) { - AppListItemModel* item = apps_model_->GetItemAt(index); + AppListItemModel* item = model_->apps()->GetItemAt(index); item->SetHighlighted(true); } @@ -145,7 +145,7 @@ class AppsGridViewTest : public testing::Test { } AppListItemView* GetItemViewForPoint(const gfx::Point& point) { - for (size_t i = 0; i < apps_model_->item_count(); ++i) { + for (size_t i = 0; i < model_->apps()->item_count(); ++i) { AppListItemView* view = GetItemViewAt(i); if (view->bounds().Contains(point)) return view; @@ -154,7 +154,7 @@ class AppsGridViewTest : public testing::Test { } gfx::Rect GetItemTileRectAt(int row, int col) { - DCHECK_GT(apps_model_->item_count(), 0u); + DCHECK_GT(model_->apps()->item_count(), 0u); gfx::Insets insets(apps_grid_view_->GetInsets()); gfx::Rect rect(gfx::Point(insets.left(), insets.top()), @@ -170,8 +170,10 @@ class AppsGridViewTest : public testing::Test { AppListItemView* view = GetItemViewForPoint(from); DCHECK(view); - gfx::Point translated_from = from.Subtract(view->bounds().origin()); - gfx::Point translated_to = to.Subtract(view->bounds().origin()); + gfx::Point translated_from = gfx::PointAtOffsetFromOrigin( + from - view->bounds().origin()); + gfx::Point translated_to = gfx::PointAtOffsetFromOrigin( + to - view->bounds().origin()); ui::MouseEvent pressed_event(ui::ET_MOUSE_PRESSED, translated_from, translated_from, 0); @@ -182,7 +184,7 @@ class AppsGridViewTest : public testing::Test { apps_grid_view_->UpdateDrag(view, pointer, drag_event); } - scoped_ptr<AppListModel::Apps> apps_model_; + scoped_ptr<AppListModel> model_; scoped_ptr<PaginationModel> pagination_model_; scoped_ptr<AppsGridView> apps_grid_view_; scoped_ptr<AppsGridViewTestApi> test_api_; @@ -200,7 +202,7 @@ TEST_F(AppsGridViewTest, CreatePage) { EXPECT_EQ(kPages, pagination_model_->total_pages()); // Adds one more and gets a new page created. - apps_model_->Add(CreateItem(std::string("Extra"))); + model_->apps()->Add(CreateItem(std::string("Extra"))); EXPECT_EQ(kPages + 1, pagination_model_->total_pages()); } @@ -222,7 +224,7 @@ TEST_F(AppsGridViewTest, EnsureHighlightedVisible) { EXPECT_EQ(1, pagination_model_->selected_page()); // Highlight last one in the model and last page should be selected. - HighlightItemAt(apps_model_->item_count() - 1); + HighlightItemAt(model_->apps()->item_count() - 1); EXPECT_EQ(kPages - 1, pagination_model_->selected_page()); } @@ -234,7 +236,7 @@ TEST_F(AppsGridViewTest, RemoveSelectedLastApp) { AppListItemView* last_view = GetItemViewAt(kLastItemIndex); apps_grid_view_->SetSelectedView(last_view); - apps_model_->DeleteAt(kLastItemIndex); + model_->apps()->DeleteAt(kLastItemIndex); EXPECT_FALSE(apps_grid_view_->IsSelectedView(last_view)); @@ -269,7 +271,7 @@ TEST_F(AppsGridViewTest, MouseDrag) { // Deleting an item keeps remaining intact. SimulateDrag(AppsGridView::MOUSE, from, to); - apps_model_->DeleteAt(1); + model_->apps()->DeleteAt(1); apps_grid_view_->EndDrag(false); EXPECT_EQ(std::string("Item 1,Item 2,Item 3"), GetModelContent()); @@ -277,7 +279,7 @@ TEST_F(AppsGridViewTest, MouseDrag) { // Adding a launcher item cancels the drag and respects the order. SimulateDrag(AppsGridView::MOUSE, from, to); - apps_model_->Add(CreateItem(std::string("Extra"))); + model_->apps()->Add(CreateItem(std::string("Extra"))); apps_grid_view_->EndDrag(false); EXPECT_EQ(std::string("Item 1,Item 2,Item 3,Extra"), GetModelContent()); diff --git a/ui/app_list/contents_view.cc b/ui/app_list/contents_view.cc index 5aa9c58..cbb4c2a 100644 --- a/ui/app_list/contents_view.cc +++ b/ui/app_list/contents_view.cc @@ -74,7 +74,7 @@ ContentsView::~ContentsView() { void ContentsView::SetModel(AppListModel* model) { if (model) { - GetAppsGridView(view_model_.get())->SetModel(model->apps()); + GetAppsGridView(view_model_.get())->SetModel(model); GetSearchResultListView(view_model_.get())->SetResults(model->results()); } else { GetAppsGridView(view_model_.get())->SetModel(NULL); diff --git a/ui/app_list/pulsing_block_view.cc b/ui/app_list/pulsing_block_view.cc new file mode 100644 index 0000000..30059d1 --- /dev/null +++ b/ui/app_list/pulsing_block_view.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2012 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/pulsing_block_view.h" + +#include <vector> + +#include "base/rand_util.h" +#include "third_party/skia/include/core/SkColor.h" +#include "ui/compositor/layer_animation_element.h" +#include "ui/compositor/layer_animation_sequence.h" +#include "ui/compositor/layer_animator.h" +#include "ui/compositor/layer.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/transform_util.h" + +namespace { + +const SkColor kBlockColor = SkColorSetRGB(225, 225, 225); +const int kBlockSize = 64; + +const int kAnimationDurationInMs = 600; +const float kAnimationOpacity[] = { 0.4f, 0.8f, 0.4f }; +const float kAnimationScale[] = { 0.8f, 1.0f, 0.8f }; + +void SchedulePulsingAnimation(ui::Layer* layer) { + DCHECK(layer); + DCHECK_EQ(arraysize(kAnimationOpacity), arraysize(kAnimationScale)); + + scoped_ptr<ui::LayerAnimationSequence> opacity_sequence( + new ui::LayerAnimationSequence()); + scoped_ptr<ui::LayerAnimationSequence> transform_sequence( + new ui::LayerAnimationSequence()); + + // The animations loop infinitely. + opacity_sequence->set_is_cyclic(true); + transform_sequence->set_is_cyclic(true); + + const gfx::Rect local_bounds(layer->bounds().size()); + for (size_t i = 0; i < arraysize(kAnimationOpacity); ++i) { + opacity_sequence->AddElement( + ui::LayerAnimationElement::CreateOpacityElement( + kAnimationOpacity[i], + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); + transform_sequence->AddElement( + ui::LayerAnimationElement::CreateTransformElement( + gfx::GetScaleTransform(local_bounds.CenterPoint(), + kAnimationScale[i]), + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); + } + + ui::LayerAnimationElement::AnimatableProperties opacity_properties; + opacity_properties.insert(ui::LayerAnimationElement::OPACITY); + opacity_sequence->AddElement( + ui::LayerAnimationElement::CreatePauseElement( + opacity_properties, + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); + + ui::LayerAnimationElement::AnimatableProperties transform_properties; + transform_properties.insert(ui::LayerAnimationElement::TRANSFORM); + transform_sequence->AddElement( + ui::LayerAnimationElement::CreatePauseElement( + transform_properties, + base::TimeDelta::FromMilliseconds(kAnimationDurationInMs))); + + std::vector<ui::LayerAnimationSequence*> animations; + animations.push_back(opacity_sequence.release()); + animations.push_back(transform_sequence.release()); + layer->GetAnimator()->ScheduleTogether(animations); +} + +} // namespace + +namespace app_list { + +PulsingBlockView::PulsingBlockView(const gfx::Size& size, bool start_delay) { +#if !defined(OS_WIN) + SetPaintToLayer(true); + SetFillsBoundsOpaquely(false); + + const int max_delay = kAnimationDurationInMs * arraysize(kAnimationOpacity); + const int delay = start_delay ? base::RandInt(0, max_delay) : 0; + start_delay_timer_.Start( + FROM_HERE, + base::TimeDelta::FromMilliseconds(delay), + this, &PulsingBlockView::OnStartDelayTimer); +#else + NOTREACHED() << "Pulsing animation is not supported on Windows"; +#endif +} + +PulsingBlockView::~PulsingBlockView() { +} + +void PulsingBlockView::OnStartDelayTimer() { + SchedulePulsingAnimation(layer()); +} + +void PulsingBlockView::OnPaint(gfx::Canvas* canvas) { + gfx::Rect rect(GetContentsBounds()); + rect.ClampToCenteredSize(gfx::Size(kBlockSize, kBlockSize)); + canvas->FillRect(rect, kBlockColor); +} + +} // namespace app_list diff --git a/ui/app_list/pulsing_block_view.h b/ui/app_list/pulsing_block_view.h new file mode 100644 index 0000000..bac2cac --- /dev/null +++ b/ui/app_list/pulsing_block_view.h @@ -0,0 +1,40 @@ +// Copyright (c) 2012 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_PULSING_BLOCK_VIEW_H_ +#define UI_APP_LIST_PULSING_BLOCK_VIEW_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/timer.h" +#include "ui/views/view.h" + +namespace gfx { +class Size; +} + +namespace app_list { + +// PulsingBlockView shows a pulsing white block via layer animation. +class PulsingBlockView : public views::View { + public: + // Constructs a PulsingBlockView of |size|. If |start_delay| is true, + // starts the pulsing animation after a random delay. + PulsingBlockView(const gfx::Size& size, bool start_delay); + virtual ~PulsingBlockView(); + + private: + void OnStartDelayTimer(); + + // views::View overrides: + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; + + base::OneShotTimer<PulsingBlockView> start_delay_timer_; + + DISALLOW_COPY_AND_ASSIGN(PulsingBlockView); +}; + +} // namespace app_list + +#endif // UI_APP_LIST_PULSING_BLOCK_VIEW_H_ |