summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/ui/app_list/app_list_view_delegate.cc10
-rw-r--r--chrome/browser/ui/app_list/app_list_view_delegate.h8
-rw-r--r--chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.cc30
-rw-r--r--chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h36
-rw-r--r--chrome/chrome_browser_ui.gypi2
-rw-r--r--ui/app_list/app_list.gyp3
-rw-r--r--ui/app_list/app_list_model.cc22
-rw-r--r--ui/app_list/app_list_model.h16
-rw-r--r--ui/app_list/app_list_model_observer.h23
-rw-r--r--ui/app_list/apps_grid_view.cc76
-rw-r--r--ui/app_list/apps_grid_view.h18
-rw-r--r--ui/app_list/apps_grid_view_unittest.cc34
-rw-r--r--ui/app_list/contents_view.cc2
-rw-r--r--ui/app_list/pulsing_block_view.cc106
-rw-r--r--ui/app_list/pulsing_block_view.h40
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_