summaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-22 11:16:58 +0000
committerxiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-22 11:16:58 +0000
commit61af999511b96b3a9adffdb657f312771e8658e3 (patch)
tree8c27d4fb8ec795c887549f33d73b6531af0a1f51 /ui
parentc649d52d0c673eae23af01f266ae8c657d8e8fd7 (diff)
downloadchromium_src-61af999511b96b3a9adffdb657f312771e8658e3.zip
chromium_src-61af999511b96b3a9adffdb657f312771e8658e3.tar.gz
chromium_src-61af999511b96b3a9adffdb657f312771e8658e3.tar.bz2
[Aura] Implement views-based applist.
Implement a views-based applist behind "aura-views-applist" flag. BUG=98308,105913 TEST=Views-based app list should have no lag. Review URL: http://codereview.chromium.org/8890049 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@115515 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r--ui/aura_shell/app_list/app_list.cc (renamed from ui/aura_shell/app_list.cc)30
-rw-r--r--ui/aura_shell/app_list/app_list.h (renamed from ui/aura_shell/app_list.h)6
-rw-r--r--ui/aura_shell/app_list/app_list_groups_view.cc243
-rw-r--r--ui/aura_shell/app_list/app_list_groups_view.h84
-rw-r--r--ui/aura_shell/app_list/app_list_item_group_model.cc25
-rw-r--r--ui/aura_shell/app_list/app_list_item_group_model.h46
-rw-r--r--ui/aura_shell/app_list/app_list_item_group_view.cc115
-rw-r--r--ui/aura_shell/app_list/app_list_item_group_view.h65
-rw-r--r--ui/aura_shell/app_list/app_list_item_model.cc37
-rw-r--r--ui/aura_shell/app_list/app_list_item_model.h54
-rw-r--r--ui/aura_shell/app_list/app_list_item_model_observer.h27
-rw-r--r--ui/aura_shell/app_list/app_list_item_view.cc162
-rw-r--r--ui/aura_shell/app_list/app_list_item_view.h72
-rw-r--r--ui/aura_shell/app_list/app_list_item_view_listener.h27
-rw-r--r--ui/aura_shell/app_list/app_list_model.cc32
-rw-r--r--ui/aura_shell/app_list/app_list_model.h41
-rw-r--r--ui/aura_shell/app_list/app_list_view.cc80
-rw-r--r--ui/aura_shell/app_list/app_list_view.h61
-rw-r--r--ui/aura_shell/app_list/app_list_view_delegate.h27
-rw-r--r--ui/aura_shell/app_list/drop_shadow_label.cc118
-rw-r--r--ui/aura_shell/app_list/drop_shadow_label.h54
-rw-r--r--ui/aura_shell/aura_shell.gyp24
-rw-r--r--ui/aura_shell/aura_shell_switches.cc3
-rw-r--r--ui/aura_shell/aura_shell_switches.h1
-rw-r--r--ui/aura_shell/examples/app_list.cc163
-rw-r--r--ui/aura_shell/examples/aura_shell_main.cc43
-rw-r--r--ui/aura_shell/examples/example_factory.h13
-rw-r--r--ui/aura_shell/shell.cc2
-rw-r--r--ui/aura_shell/shell_delegate.h11
-rw-r--r--ui/aura_shell/test/test_shell_delegate.cc7
-rw-r--r--ui/aura_shell/test/test_shell_delegate.h2
-rw-r--r--ui/base/models/list_model.h116
-rw-r--r--ui/base/models/list_model_observer.h31
-rw-r--r--ui/base/models/list_model_unittest.cc149
-rw-r--r--ui/ui.gyp2
-rw-r--r--ui/ui_unittests.gypi1
36 files changed, 1933 insertions, 41 deletions
diff --git a/ui/aura_shell/app_list.cc b/ui/aura_shell/app_list/app_list.cc
index 6e21601..b68acb9 100644
--- a/ui/aura_shell/app_list.cc
+++ b/ui/aura_shell/app_list/app_list.cc
@@ -2,13 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/aura_shell/app_list.h"
+#include "ui/aura_shell/app_list/app_list.h"
#include "base/bind.h"
+#include "base/command_line.h"
#include "ui/aura/event.h"
#include "ui/aura/window.h"
-#include "ui/aura_shell/shell.h"
+#include "ui/aura_shell/app_list/app_list_model.h"
+#include "ui/aura_shell/app_list/app_list_view.h"
+#include "ui/aura_shell/aura_shell_switches.h"
#include "ui/aura_shell/shell_delegate.h"
+#include "ui/aura_shell/shell.h"
#include "ui/aura_shell/shell_window_ids.h"
#include "ui/gfx/screen.h"
@@ -25,7 +29,7 @@ gfx::Rect GetPreferredBounds(bool show) {
gfx::Point cursor = gfx::Screen::GetCursorScreenPoint();
gfx::Rect work_area = gfx::Screen::GetMonitorWorkAreaNearestPoint(cursor);
gfx::Rect widget_bounds(work_area);
- widget_bounds.Inset(150, 100);
+ widget_bounds.Inset(100, 100);
if (!show)
widget_bounds.Offset(0, kMoveUpAnimationOffset);
@@ -61,9 +65,23 @@ void AppList::SetVisible(bool visible) {
if (widget_) {
ScheduleAnimation();
} else if (is_visible_ && !set_widget_factory_.HasWeakPtrs()) {
- Shell::GetInstance()->delegate()->RequestAppListWidget(
- GetPreferredBounds(false),
- base::Bind(&AppList::SetWidget, set_widget_factory_.GetWeakPtr()));
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAuraViewsAppList)) {
+ scoped_ptr<AppListModel> model(new AppListModel);
+ Shell::GetInstance()->delegate()->BuildAppListModel(model.get());
+
+ // AppListModel and AppListViewDelegate are owned by AppListView. They
+ // will be released with AppListView on close.
+ new AppListView(
+ model.release(),
+ Shell::GetInstance()->delegate()->CreateAppListViewDelegate(),
+ GetPreferredBounds(false),
+ base::Bind(&AppList::SetWidget, set_widget_factory_.GetWeakPtr()));
+ } else {
+ Shell::GetInstance()->delegate()->RequestAppListWidget(
+ GetPreferredBounds(false),
+ base::Bind(&AppList::SetWidget, set_widget_factory_.GetWeakPtr()));
+ }
}
}
diff --git a/ui/aura_shell/app_list.h b/ui/aura_shell/app_list/app_list.h
index 398eea3..0802258 100644
--- a/ui/aura_shell/app_list.h
+++ b/ui/aura_shell/app_list/app_list.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef UI_AURA_SHELL_APP_LIST_H_
-#define UI_AURA_SHELL_APP_LIST_H_
+#ifndef UI_AURA_SHELL_APP_LIST_APP_LIST_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_H_
#pragma once
#include "base/basictypes.h"
@@ -80,4 +80,4 @@ class AppList : public aura::EventFilter,
} // namespace internal
} // namespace aura_shell
-#endif // UI_AURA_SHELL_APP_LIST_H_
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_H_
diff --git a/ui/aura_shell/app_list/app_list_groups_view.cc b/ui/aura_shell/app_list/app_list_groups_view.cc
new file mode 100644
index 0000000..e448784
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_groups_view.cc
@@ -0,0 +1,243 @@
+// Copyright (c) 2011 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/aura_shell/app_list/app_list_groups_view.h"
+
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/aura_shell/app_list/app_list_item_group_model.h"
+#include "ui/aura_shell/app_list/app_list_item_group_view.h"
+#include "ui/aura_shell/app_list/app_list_item_view.h"
+#include "ui/aura_shell/app_list/app_list_model.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/views/animation/bounds_animator.h"
+#include "ui/views/controls/button/text_button.h"
+#include "ui/views/layout/box_layout.h"
+
+namespace aura_shell {
+
+namespace {
+
+const SkColor kPageHeaderColor = SkColorSetARGB(0xFF, 0xB2, 0xB2, 0xB2);
+const SkColor kSelectedPageHeaderColor = SK_ColorWHITE;
+
+// Creates page headers view that hosts page title buttons.
+views::View* CreatePageHeader() {
+ views::View* header = new views::View;
+ header->SetLayoutManager(
+ new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
+ return header;
+}
+
+// Creates page header button view that shows page title.
+views::View* CreatePageHeaderButton(views::ButtonListener* listener,
+ const std::string& title ) {
+ views::TextButton* button = new views::TextButton(listener,
+ UTF8ToUTF16(title));
+
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ button->SetFont(rb.GetFont(ResourceBundle::BaseFont).DeriveFont(
+ 2, gfx::Font::BOLD));
+ button->SetEnabledColor(kPageHeaderColor);
+ return button;
+}
+
+// Gets preferred bounds of buttons and page.
+void GetPageAndHeaderBounds(views::View* parent,
+ views::View* buttons,
+ views::View* page,
+ gfx::Rect* buttons_bounds,
+ gfx::Rect* page_bounds) {
+ gfx::Rect content_bounds = parent->GetContentsBounds();
+
+ if (buttons) {
+ gfx::Size buttons_size = buttons->GetPreferredSize();
+ if (buttons_bounds) {
+ buttons_bounds->SetRect(
+ (content_bounds.width() - buttons_size.width()) / 2,
+ content_bounds.bottom() - buttons_size.height(),
+ buttons_size.width(), buttons_size.height());
+ }
+
+ content_bounds.set_height(
+ std::max(0, content_bounds.height() - buttons_size.height()));
+ }
+
+ if (page_bounds) {
+ gfx::Size page_size = page->GetPreferredSize();
+ *page_bounds = content_bounds.Center(page_size);
+ }
+}
+
+} // namespace
+
+AppListGroupsView::AppListGroupsView(AppListModel* model,
+ AppListItemViewListener* listener)
+ : model_(model),
+ listener_(listener),
+ page_buttons_(NULL),
+ current_page_(0) {
+ animator_.reset(new views::BoundsAnimator(this));
+ model_->AddObserver(this);
+ Update();
+}
+
+AppListGroupsView::~AppListGroupsView() {
+ model_->RemoveObserver(this);
+}
+
+views::View* AppListGroupsView::GetFocusedTile() const {
+ AppListItemGroupView* page = GetCurrentPageView();
+ return page ? page->GetFocusedTile() : NULL;
+}
+
+void AppListGroupsView::Update() {
+ current_page_ = 0;
+ page_buttons_ = NULL;
+ RemoveAllChildViews(true);
+
+ int page_count = model_->group_count();
+ if (!page_count)
+ return;
+
+ if (page_count > 1)
+ AddChildView(page_buttons_ = CreatePageHeader());
+
+ for (int i = 0; i < page_count; ++i) {
+ AppListItemGroupModel* group = model_->GetGroup(i);
+ AddPage(group->title(), new AppListItemGroupView(group, listener_));
+ }
+
+ if (!size().IsEmpty())
+ Layout();
+ SetCurrentPage(0);
+}
+
+void AppListGroupsView::AddPage(const std::string& title,
+ AppListItemGroupView* page) {
+ pages_.push_back(page);
+ AddChildView(page);
+
+ if (page_buttons_)
+ page_buttons_->AddChildView(CreatePageHeaderButton(this, title));
+}
+
+int AppListGroupsView::GetPreferredTilesPerRow() const {
+ return std::max(1, width() / AppListItemView::kTileSize);
+}
+
+AppListItemGroupView* AppListGroupsView::GetCurrentPageView() const {
+ return static_cast<size_t>(current_page_) < pages_.size() ?
+ pages_[current_page_] : NULL;
+}
+
+void AppListGroupsView::SetCurrentPage(int page) {
+ int previous_page = current_page_;
+ current_page_ = page;
+
+ // Updates page buttons.
+ if (page_buttons_) {
+ for (int i = 0; i < page_buttons_->child_count(); ++i) {
+ views::TextButton* button = static_cast<views::TextButton*>(
+ page_buttons_->child_at(i));
+
+ button->SetEnabledColor(i == current_page_ ?
+ kSelectedPageHeaderColor : kPageHeaderColor);
+ }
+ page_buttons_->SchedulePaint();
+ }
+
+ // Gets sliding animation direction.
+ int dir = previous_page < current_page_ ? -1 :
+ previous_page > current_page_ ? 1 : 0;
+
+ // Skips animation if no sliding needed or no valid size.
+ if (dir == 0 || size().IsEmpty())
+ return;
+
+ animator_->Cancel();
+
+ // Makes sure new page has correct layout and focus to its focused tile.
+ AppListItemGroupView* current_view = GetCurrentPageView();
+ current_view->SetTilesPerRow(GetPreferredTilesPerRow());
+ views::View* tile = current_view->GetFocusedTile();
+ if (tile)
+ tile->RequestFocus();
+
+ // Prepares current page before animation.
+ gfx::Rect current_page_bounds;
+ GetPageAndHeaderBounds(this, page_buttons_, current_view,
+ NULL, &current_page_bounds);
+ current_page_bounds.Offset(- dir * width(), 0);
+ current_view->SetBoundsRect(current_page_bounds);
+
+ // Schedules animations to slide out previous page and slide in current page.
+ AppListItemGroupView* previous_view = pages_[previous_page];
+ gfx::Rect previous_page_bounds = previous_view->bounds();
+ previous_page_bounds.Offset(dir * width(), 0);
+ animator_->AnimateViewTo(previous_view, previous_page_bounds);
+
+ current_page_bounds.Offset(dir * width(), 0);
+ animator_->AnimateViewTo(current_view, current_page_bounds);
+}
+
+void AppListGroupsView::Layout() {
+ AppListItemGroupView* page = GetCurrentPageView();
+ if (!page)
+ return;
+
+ page->SetTilesPerRow(GetPreferredTilesPerRow());
+
+ gfx::Rect buttons_bounds;
+ gfx::Rect page_bounds;
+ GetPageAndHeaderBounds(this, page_buttons_, page,
+ &buttons_bounds, &page_bounds);
+
+ if (page_buttons_)
+ page_buttons_->SetBoundsRect(buttons_bounds);
+
+ page->SetBoundsRect(page_bounds);
+}
+
+bool AppListGroupsView::OnKeyPressed(const views::KeyEvent& event) {
+ if (event.IsControlDown()) {
+ switch (event.key_code()) {
+ case ui::VKEY_LEFT:
+ if (current_page_ > 0)
+ SetCurrentPage(current_page_ - 1);
+ return true;
+ case ui::VKEY_RIGHT:
+ if (static_cast<size_t>(current_page_ + 1) < pages_.size())
+ SetCurrentPage(current_page_ + 1);
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+void AppListGroupsView::ButtonPressed(views::Button* sender,
+ const views::Event& event) {
+ DCHECK(page_buttons_);
+ for (int i = 0; i < page_buttons_->child_count(); ++i) {
+ if (page_buttons_->child_at(i) == sender)
+ SetCurrentPage(i);
+ }
+}
+
+void AppListGroupsView::ListItemsAdded(int start, int count) {
+ Update();
+}
+
+void AppListGroupsView::ListItemsRemoved(int start, int count) {
+ Update();
+}
+
+void AppListGroupsView::ListItemsChanged(int start, int count) {
+ NOTREACHED();
+}
+
+} // namespace aura_shell
diff --git a/ui/aura_shell/app_list/app_list_groups_view.h b/ui/aura_shell/app_list/app_list_groups_view.h
new file mode 100644
index 0000000..bc2839d
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_groups_view.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_GROUPS_VIEW_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_GROUPS_VIEW_H_
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/list_model_observer.h"
+#include "ui/views/controls/button/button.h"
+#include "ui/views/view.h"
+
+namespace views {
+class BoundsAnimator;
+}
+
+namespace aura_shell {
+
+class AppListItemGroupView;
+class AppListItemViewListener;
+class AppListModel;
+
+// AppListGroupsView displays the UI for an AppListModel. If there are more than
+// one group in the model , a button strip is displayed to allow user to switch
+// between pages.
+class AURA_SHELL_EXPORT AppListGroupsView : public views::View,
+ public views::ButtonListener,
+ public ui::ListModelObserver {
+ public:
+ AppListGroupsView(AppListModel* model,
+ AppListItemViewListener* listener);
+ virtual ~AppListGroupsView();
+
+ // Gets current focused tile.
+ views::View* GetFocusedTile() const;
+
+ private:
+ // Updates from model.
+ void Update();
+
+ // Adds a result group page.
+ void AddPage(const std::string& title, AppListItemGroupView* page);
+
+ // Gets preferred number of tiles per row.
+ int GetPreferredTilesPerRow() const;
+
+ // Gets current result page.
+ AppListItemGroupView* GetCurrentPageView() const;
+
+ // Sets current result page.
+ void SetCurrentPage(int page);
+
+ // Overridden from views::View:
+ virtual void Layout() OVERRIDE;
+ virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE;
+
+ // Overridden from views::ButtonListener:
+ virtual void ButtonPressed(views::Button* sender,
+ const views::Event& event) OVERRIDE;
+
+ // Overridden from ListModelObserver:
+ virtual void ListItemsAdded(int start, int count) OVERRIDE;
+ virtual void ListItemsRemoved(int start, int count) OVERRIDE;
+ virtual void ListItemsChanged(int start, int count) OVERRIDE;
+
+ AppListModel* model_; // Owned by parent AppListView.
+ AppListItemViewListener* listener_;
+
+ std::vector<AppListItemGroupView*> pages_;
+ views::View* page_buttons_;
+ int current_page_;
+
+ scoped_ptr<views::BoundsAnimator> animator_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListGroupsView);
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_GROUPS_VIEW_H_
diff --git a/ui/aura_shell/app_list/app_list_item_group_model.cc b/ui/aura_shell/app_list/app_list_item_group_model.cc
new file mode 100644
index 0000000..61e3b9e
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_group_model.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 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/aura_shell/app_list/app_list_item_group_model.h"
+
+namespace aura_shell {
+
+AppListItemGroupModel::AppListItemGroupModel(const std::string& title)
+ : title_(title) {
+}
+
+AppListItemGroupModel::~AppListItemGroupModel() {
+}
+
+void AppListItemGroupModel::AddItem(AppListItemModel* item) {
+ items_.Add(item);
+}
+
+AppListItemModel* AppListItemGroupModel::GetItem(int index) {
+ DCHECK(index >= 0 && index < item_count());
+ return items_.item_at(index);
+}
+
+} // namespace aura_shell
diff --git a/ui/aura_shell/app_list/app_list_item_group_model.h b/ui/aura_shell/app_list/app_list_item_group_model.h
new file mode 100644
index 0000000..6476742
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_group_model.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_ITEM_GROUP_MODEL_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_GROUP_MODEL_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "ui/aura_shell/app_list/app_list_item_model.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/list_model.h"
+
+namespace aura_shell {
+
+// AppListItemGroupModel holds a list of AppListItemModels.
+class AURA_SHELL_EXPORT AppListItemGroupModel {
+ public:
+ typedef ui::ListModel<AppListItemModel> Items;
+
+ explicit AppListItemGroupModel(const std::string& title);
+ virtual ~AppListItemGroupModel();
+
+ void AddItem(AppListItemModel* item);
+ AppListItemModel* GetItem(int index);
+
+ const std::string& title() const {
+ return title_;
+ }
+
+ int item_count() const {
+ return items_.item_count();
+ }
+
+ private:
+ const std::string title_;
+ Items items_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListItemGroupModel);
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_GROUP_MODEL_H_
diff --git a/ui/aura_shell/app_list/app_list_item_group_view.cc b/ui/aura_shell/app_list/app_list_item_group_view.cc
new file mode 100644
index 0000000..3a91672
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_group_view.cc
@@ -0,0 +1,115 @@
+// Copyright (c) 2011 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/aura_shell/app_list/app_list_item_group_view.h"
+
+#include "ui/aura_shell/app_list/app_list_item_group_model.h"
+#include "ui/aura_shell/app_list/app_list_item_view.h"
+#include "ui/views/layout/grid_layout.h"
+
+namespace aura_shell {
+
+AppListItemGroupView::AppListItemGroupView(AppListItemGroupModel* model,
+ AppListItemViewListener* listener)
+ : model_(model),
+ listener_(listener),
+ tiles_per_row_(0),
+ focused_index_(0) {
+ Update();
+}
+
+AppListItemGroupView::~AppListItemGroupView() {
+}
+
+void AppListItemGroupView::SetTilesPerRow(int tiles_per_row) {
+ if (tiles_per_row_ == tiles_per_row)
+ return;
+
+ tiles_per_row_ = tiles_per_row;
+ Update();
+}
+
+void AppListItemGroupView::Update() {
+ RemoveAllChildViews(true);
+ if (model_->item_count() == 0 || tiles_per_row_ == 0)
+ return;
+
+ views::GridLayout* layout = new views::GridLayout(this);
+ SetLayoutManager(layout);
+
+ const int kTileColumnSetId = 0;
+ views::ColumnSet* column_set = layout->AddColumnSet(kTileColumnSetId);
+ for (int i = 0; i < tiles_per_row_; ++i) {
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
+ views::GridLayout::USE_PREF, 0, 0);
+ }
+
+ for (int i = 0; i < model_->item_count(); ++i) {
+ if (i % tiles_per_row_ == 0)
+ layout->StartRow(0, kTileColumnSetId);
+
+ layout->AddView(new AppListItemView(model_->GetItem(i), listener_));
+ }
+}
+
+views::View* AppListItemGroupView::GetFocusedTile() {
+ return focused_index_ < child_count() ? child_at(focused_index_) : NULL;
+}
+
+void AppListItemGroupView::UpdateFocusedTile(views::View* tile) {
+ for (int i = 0; i < child_count(); ++i) {
+ if (child_at(i) == tile) {
+ focused_index_ = i;
+ break;
+ }
+ }
+}
+
+void AppListItemGroupView::SetFocusedTileByIndex(int index) {
+ index = std::max(0, std::min(child_count() - 1, index));
+ if (index != focused_index_)
+ child_at(index)->RequestFocus();
+}
+
+bool AppListItemGroupView::OnKeyPressed(const views::KeyEvent& event) {
+ if (!event.IsControlDown() && !event.IsShiftDown() && !event.IsAltDown()) {
+ // Arrow keys navigates in tile grid.
+ switch (event.key_code()) {
+ case ui::VKEY_LEFT:
+ if (focused_index_ > 0)
+ SetFocusedTileByIndex(focused_index_ - 1);
+ return true;
+ case ui::VKEY_RIGHT:
+ if (focused_index_ + 1 < child_count())
+ SetFocusedTileByIndex(focused_index_ + 1);
+ return true;
+ case ui::VKEY_UP:
+ if (focused_index_ - tiles_per_row_ >= 0)
+ SetFocusedTileByIndex(focused_index_ - tiles_per_row_);
+ return true;
+ case ui::VKEY_DOWN:
+ if (focused_index_ + tiles_per_row_ < child_count())
+ SetFocusedTileByIndex(focused_index_ + tiles_per_row_);
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+void AppListItemGroupView::ListItemsAdded(int start, int count) {
+ Update();
+}
+
+void AppListItemGroupView::ListItemsRemoved(int start, int count) {
+ Update();
+}
+
+void AppListItemGroupView::ListItemsChanged(int start, int count) {
+ NOTREACHED();
+}
+
+} // namespace aura_shell
diff --git a/ui/aura_shell/app_list/app_list_item_group_view.h b/ui/aura_shell/app_list/app_list_item_group_view.h
new file mode 100644
index 0000000..6718f9d
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_group_view.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_ITEM_GROUP_VIEW_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_GROUP_VIEW_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/list_model_observer.h"
+#include "ui/views/view.h"
+
+namespace aura_shell {
+
+class AppListItemGroupModel;
+class AppListItemViewListener;
+
+// AppListItemGroupView displays its children tiles in a grid.
+class AURA_SHELL_EXPORT AppListItemGroupView
+ : public views::View,
+ public ui::ListModelObserver {
+ public:
+ AppListItemGroupView(AppListItemGroupModel* model,
+ AppListItemViewListener* listener);
+ virtual ~AppListItemGroupView();
+
+ // Sets tiles per row.
+ void SetTilesPerRow(int tiles_per_row);
+
+ // Gets currently focused tile.
+ views::View* GetFocusedTile();
+
+ // Updates tiles page when a tile gets focus.
+ void UpdateFocusedTile(views::View* tile);
+
+ private:
+ // Updates from model.
+ void Update();
+
+ // Sets focused tile by index.
+ void SetFocusedTileByIndex(int index);
+
+ // Overridden from views::View:
+ virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE;
+
+ // Overridden from ListModelObserver:
+ virtual void ListItemsAdded(int start, int count) OVERRIDE;
+ virtual void ListItemsRemoved(int start, int count) OVERRIDE;
+ virtual void ListItemsChanged(int start, int count) OVERRIDE;
+
+ AppListItemGroupModel* model_;
+ AppListItemViewListener* listener_;
+
+ // Tiles per row.
+ int tiles_per_row_;
+
+ // Index of focused tile view.
+ int focused_index_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListItemGroupView);
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_GROUP_VIEW_H_
diff --git a/ui/aura_shell/app_list/app_list_item_model.cc b/ui/aura_shell/app_list/app_list_item_model.cc
new file mode 100644
index 0000000..a93bb8b
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_model.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2011 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/aura_shell/app_list/app_list_item_model.h"
+
+#include "ui/aura_shell/app_list/app_list_item_model_observer.h"
+
+namespace aura_shell {
+
+AppListItemModel::AppListItemModel() {
+}
+
+AppListItemModel::~AppListItemModel() {
+}
+
+void AppListItemModel::SetIcon(const SkBitmap& icon) {
+ icon_ = icon;
+ FOR_EACH_OBSERVER(AppListItemModelObserver, observers_,
+ ItemIconChanged());
+}
+
+void AppListItemModel::SetTitle(const std::string& title) {
+ title_ = title;
+ FOR_EACH_OBSERVER(AppListItemModelObserver, observers_,
+ ItemTitleChanged());
+}
+
+void AppListItemModel::AddObserver(AppListItemModelObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void AppListItemModel::RemoveObserver(AppListItemModelObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+} // namespace aura_shell
diff --git a/ui/aura_shell/app_list/app_list_item_model.h b/ui/aura_shell/app_list/app_list_item_model.h
new file mode 100644
index 0000000..3d5c38c
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_model.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_ITEM_MODEL_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_MODEL_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/observer_list.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AppListItemModelObserver;
+
+// AppListItemModel provides icon and title to be shown in a TileView and
+// action to be executed when the TileView is activated (clicked or enter
+// key it hit).
+class AURA_SHELL_EXPORT AppListItemModel {
+ public:
+ AppListItemModel();
+ virtual ~AppListItemModel();
+
+ // Changes icon and title for the model.
+ void SetIcon(const SkBitmap& icon);
+ void SetTitle(const std::string& title);
+
+ void AddObserver(AppListItemModelObserver* observer);
+ void RemoveObserver(AppListItemModelObserver* observer);
+
+ const SkBitmap& icon() const {
+ return icon_;
+ }
+
+ const std::string& title() const {
+ return title_;
+ }
+
+ private:
+ SkBitmap icon_;
+ std::string title_;
+
+ ObserverList<AppListItemModelObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListItemModel);
+};
+
+} // namespace aura_shell
+
+#endif // #define UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_MODEL_H_
diff --git a/ui/aura_shell/app_list/app_list_item_model_observer.h b/ui/aura_shell/app_list/app_list_item_model_observer.h
new file mode 100644
index 0000000..f43d102
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_model_observer.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_ITEM_MODEL_OBSERVER_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_MODEL_OBSERVER_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AURA_SHELL_EXPORT AppListItemModelObserver {
+ public:
+ // Invoked after app list item's icon is changed.
+ virtual void ItemIconChanged() = 0;
+
+ // Invoked after app list item's title is changed.
+ virtual void ItemTitleChanged() = 0;
+
+ protected:
+ virtual ~AppListItemModelObserver() {}
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_MODEL_OBSERVER_H_
diff --git a/ui/aura_shell/app_list/app_list_item_view.cc b/ui/aura_shell/app_list/app_list_item_view.cc
new file mode 100644
index 0000000..afb080b
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_view.cc
@@ -0,0 +1,162 @@
+// Copyright (c) 2011 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/aura_shell/app_list/app_list_item_view.h"
+
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/aura_shell/app_list/app_list_item_group_view.h"
+#include "ui/aura_shell/app_list/app_list_item_model.h"
+#include "ui/aura_shell/app_list/app_list_item_view_listener.h"
+#include "ui/aura_shell/app_list/drop_shadow_label.h"
+#include "ui/base/resource/resource_bundle.h"
+#include "ui/gfx/canvas.h"
+#include "ui/gfx/font.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+
+namespace {
+
+const double kFocusedScale = 1.1;
+
+const SkColor kTitleColor = SK_ColorWHITE;
+
+gfx::Font GetTitleFont() {
+ static gfx::Font* font = NULL;
+ if (!font) {
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ font = new gfx::Font(rb.GetFont(ResourceBundle::BaseFont).DeriveFont(
+ 2, gfx::Font::BOLD));
+ }
+ return *font;
+}
+
+} // namespace
+
+AppListItemView::AppListItemView(AppListItemModel* model,
+ AppListItemViewListener* listener)
+ : model_(model),
+ listener_(listener),
+ icon_(new views::ImageView),
+ title_(new DropShadowLabel) {
+ set_focusable(true);
+
+ title_->SetFont(GetTitleFont());
+ title_->SetBackgroundColor(0);
+ title_->SetEnabledColor(kTitleColor);
+
+ AddChildView(icon_);
+ AddChildView(title_);
+
+ ItemIconChanged();
+ ItemTitleChanged();
+ model_->AddObserver(this);
+}
+
+AppListItemView::~AppListItemView() {
+ model_->RemoveObserver(this);
+}
+
+void AppListItemView::NotifyActivated(int event_flags) {
+ if (listener_)
+ listener_->AppListItemActivated(this, event_flags);
+}
+
+void AppListItemView::ItemIconChanged() {
+ icon_->SetImage(model_->icon());
+}
+
+void AppListItemView::ItemTitleChanged() {
+ title_->SetText(UTF8ToUTF16(model_->title()));
+}
+
+gfx::Size AppListItemView::GetPreferredSize() {
+ return gfx::Size(kTileSize, kTileSize);
+}
+
+void AppListItemView::Layout() {
+ gfx::Rect rect(GetContentsBounds());
+ gfx::Size title_size = title_->GetPreferredSize();
+
+ if (!HasFocus()) {
+ const int horiz_padding = (kTileSize - kIconSize) / 2;
+ const int vert_padding = (kTileSize - title_size.height() - kIconSize) / 2;
+ rect.Inset(horiz_padding, vert_padding);
+ }
+
+ icon_->SetBounds(rect.x(), rect.y(),
+ rect.width(), rect.height() - title_size.height());
+
+ title_->SetBounds(rect.x(), rect.bottom() - title_size.height(),
+ rect.width(), title_size.height());
+}
+
+void AppListItemView::OnFocus() {
+ View::OnFocus();
+
+ gfx::Size icon_size = icon_->GetPreferredSize();
+ icon_size.set_width(icon_size.width() * kFocusedScale);
+ icon_size.set_height(icon_size.height() * kFocusedScale);
+
+ gfx::Size max_size = GetPreferredSize();
+ if (icon_size.width() > max_size.width() ||
+ icon_size.height() > max_size.height()) {
+ double aspect =
+ static_cast<double>(icon_size.width()) / icon_size.height();
+
+ if (aspect > 1) {
+ icon_size.set_width(max_size.width());
+ icon_size.set_height(icon_size.width() / aspect);
+ } else {
+ icon_size.set_height(max_size.height());
+ icon_size.set_width(icon_size.height() * aspect);
+ }
+ }
+
+ icon_->SetImageSize(icon_size);
+ Layout();
+
+ AppListItemGroupView* group_view =
+ static_cast<AppListItemGroupView*>(parent());
+ group_view->UpdateFocusedTile(this);
+}
+
+void AppListItemView::OnBlur() {
+ icon_->ResetImageSize();
+ Layout();
+ SchedulePaint();
+}
+
+bool AppListItemView::OnKeyPressed(const views::KeyEvent& event) {
+ if (event.key_code() == ui::VKEY_RETURN) {
+ NotifyActivated(event.flags());
+ return true;
+ }
+
+ return false;
+}
+
+bool AppListItemView::OnMousePressed(const views::MouseEvent& event) {
+ views::View* hit_view = GetEventHandlerForPoint(event.location());
+ bool hit = hit_view != this;
+ if (hit)
+ RequestFocus();
+
+ return hit;
+}
+
+void AppListItemView::OnMouseReleased(const views::MouseEvent& event) {
+ views::View* hit_view = GetEventHandlerForPoint(event.location());
+ if (hit_view != this)
+ NotifyActivated(event.flags());
+}
+
+void AppListItemView::OnPaintFocusBorder(gfx::Canvas* canvas) {
+ // No focus border for AppListItemView.
+}
+
+} // namespace aura_shell
diff --git a/ui/aura_shell/app_list/app_list_item_view.h b/ui/aura_shell/app_list/app_list_item_view.h
new file mode 100644
index 0000000..3d8b6de
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_view.h
@@ -0,0 +1,72 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_ITEM_VIEW_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_VIEW_H_
+#pragma once
+
+#include "ui/aura_shell/app_list/app_list_item_model_observer.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/views/view.h"
+
+class SkBitmap;
+
+namespace views {
+class ImageView;
+class Label;
+}
+
+namespace aura_shell {
+
+class AppListItemModel;
+class AppListItemViewListener;
+
+class AURA_SHELL_EXPORT AppListItemView : public views::View,
+ public AppListItemModelObserver {
+ public:
+ AppListItemView(AppListItemModel* model,
+ AppListItemViewListener* listener);
+ virtual ~AppListItemView();
+
+ AppListItemModel* model() const {
+ return model_;
+ }
+
+ // Tile size
+ static const int kTileSize = 180;
+
+ // Preferred icon size.
+ static const int kIconSize = 128;
+
+ protected:
+ // Notifies listener when activated.
+ void NotifyActivated(int event_flags);
+
+ // AppListItemModelObserver overrides:
+ virtual void ItemIconChanged() OVERRIDE;
+ virtual void ItemTitleChanged() OVERRIDE;
+
+ // views::View overrides:
+ virtual gfx::Size GetPreferredSize() OVERRIDE;
+ virtual void Layout() OVERRIDE;
+ virtual void OnFocus() OVERRIDE;
+ virtual void OnBlur() OVERRIDE;
+ virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE;
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE;
+ virtual void OnPaintFocusBorder(gfx::Canvas* canvas) OVERRIDE;
+
+ private:
+ AppListItemModel* model_;
+ AppListItemViewListener* listener_;
+
+ views::ImageView* icon_;
+ views::Label* title_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListItemView);
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_VIEW_H_
diff --git a/ui/aura_shell/app_list/app_list_item_view_listener.h b/ui/aura_shell/app_list/app_list_item_view_listener.h
new file mode 100644
index 0000000..dfa2084
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_item_view_listener.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_ITEM_VIEW_LISTENER_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_VIEW_LISTENER_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AppListItemView;
+
+class AURA_SHELL_EXPORT AppListItemViewListener {
+ public:
+ // Invoked when an AppListeItemModelView is activated by click or enter key.
+ virtual void AppListItemActivated(AppListItemView* sender,
+ int event_flags) = 0;
+
+ protected:
+ virtual ~AppListItemViewListener() {}
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_ITEM_VIEW_LISTENER_H_
diff --git a/ui/aura_shell/app_list/app_list_model.cc b/ui/aura_shell/app_list/app_list_model.cc
new file mode 100644
index 0000000..8cc531b
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_model.cc
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 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/aura_shell/app_list/app_list_model.h"
+
+namespace aura_shell {
+
+AppListModel::AppListModel() {
+}
+
+AppListModel::~AppListModel() {
+}
+
+void AppListModel::AddGroup(AppListItemGroupModel* group) {
+ groups_.Add(group);
+}
+
+AppListItemGroupModel* AppListModel::GetGroup(int index) {
+ DCHECK(index >= 0 && index < group_count());
+ return groups_.item_at(index);
+}
+
+void AppListModel::AddObserver(ui::ListModelObserver* observer) {
+ groups_.AddObserver(observer);
+}
+
+void AppListModel::RemoveObserver(ui::ListModelObserver* observer) {
+ groups_.RemoveObserver(observer);
+}
+
+} // namespace aura_shell
diff --git a/ui/aura_shell/app_list/app_list_model.h b/ui/aura_shell/app_list/app_list_model.h
new file mode 100644
index 0000000..f077d2c
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_model.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_MODEL_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_MODEL_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "ui/aura_shell/app_list/app_list_item_group_model.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/base/models/list_model.h"
+
+namespace aura_shell {
+
+// Model for AppListView. It is consisted of a list of AppListItemGroupModels,
+// which in turn owns a list of AppListItemModels.
+class AURA_SHELL_EXPORT AppListModel {
+ public:
+ AppListModel();
+ virtual ~AppListModel();
+
+ void AddGroup(AppListItemGroupModel* group);
+ AppListItemGroupModel* GetGroup(int index);
+
+ void AddObserver(ui::ListModelObserver* observer);
+ void RemoveObserver(ui::ListModelObserver* observer);
+
+ int group_count() const {
+ return groups_.item_count();
+ }
+
+ private:
+ ui::ListModel<AppListItemGroupModel> groups_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListModel);
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_MODEL_H_
diff --git a/ui/aura_shell/app_list/app_list_view.cc b/ui/aura_shell/app_list/app_list_view.cc
new file mode 100644
index 0000000..01eb658
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_view.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 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/aura_shell/app_list/app_list_view.h"
+
+#include "ui/aura_shell/app_list/app_list_groups_view.h"
+#include "ui/aura_shell/app_list/app_list_item_view.h"
+#include "ui/aura_shell/app_list/app_list_model.h"
+#include "ui/aura_shell/app_list/app_list_view_delegate.h"
+#include "ui/aura_shell/shell.h"
+#include "ui/views/layout/fill_layout.h"
+#include "ui/views/widget/widget.h"
+
+namespace aura_shell {
+
+AppListView::AppListView(
+ AppListModel* model,
+ AppListViewDelegate* delegate,
+ const gfx::Rect& bounds,
+ const aura_shell::ShellDelegate::SetWidgetCallback& callback)
+ : model_(model),
+ delegate_(delegate) {
+ Init(bounds, callback);
+}
+
+AppListView::~AppListView() {
+}
+
+void AppListView::Close() {
+ if (GetWidget()->IsVisible())
+ Shell::GetInstance()->ToggleAppList();
+}
+
+void AppListView::Init(const gfx::Rect& bounds,
+ const ShellDelegate::SetWidgetCallback& callback) {
+ SetLayoutManager(new views::FillLayout);
+ AppListGroupsView* groups_view = new AppListGroupsView(model_.get(), this);
+ AddChildView(groups_view);
+
+ views::Widget::InitParams widget_params(
+ views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
+ widget_params.bounds = bounds;
+ widget_params.delegate = this;
+ widget_params.keep_on_top = true;
+ widget_params.transparent = true;
+
+ views::Widget* widget = new views::Widget;
+ widget->Init(widget_params);
+ widget->SetContentsView(this);
+
+ callback.Run(widget);
+ if (groups_view->GetFocusedTile())
+ groups_view->GetFocusedTile()->RequestFocus();
+}
+
+bool AppListView::OnKeyPressed(const views::KeyEvent& event) {
+ if (event.key_code() == ui::VKEY_ESCAPE) {
+ Close();
+ return true;
+ }
+
+ return false;
+}
+
+bool AppListView::OnMousePressed(const views::MouseEvent& event) {
+ // If mouse click reaches us, this means user clicks on blank area. So close.
+ Close();
+
+ return true;
+}
+
+void AppListView::AppListItemActivated(AppListItemView* sender,
+ int event_flags) {
+ if (delegate_.get())
+ delegate_->OnAppListItemActivated(sender->model(), event_flags);
+ Close();
+}
+
+} // namespace aura_shell
diff --git a/ui/aura_shell/app_list/app_list_view.h b/ui/aura_shell/app_list/app_list_view.h
new file mode 100644
index 0000000..903d3a0
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_view.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_VIEW_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_VIEW_H_
+#pragma once
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura_shell/app_list/app_list_item_view_listener.h"
+#include "ui/aura_shell/aura_shell_export.h"
+#include "ui/aura_shell/shell_delegate.h"
+#include "ui/views/widget/widget_delegate.h"
+
+namespace views {
+class View;
+}
+
+namespace aura_shell {
+
+class AppListModel;
+class AppListViewDelegate;
+
+// AppListView is the top-level view and controller of app list UI. It creates
+// and hosts a AppListModelView and passes AppListModel to it for display.
+class AURA_SHELL_EXPORT AppListView : public views::WidgetDelegateView,
+ public AppListItemViewListener {
+ public:
+ // Takes ownership of |model| and |delegate|.
+ AppListView(AppListModel* model,
+ AppListViewDelegate* delegate,
+ const gfx::Rect& bounds,
+ const ShellDelegate::SetWidgetCallback& callback);
+ virtual ~AppListView();
+
+ // Closes app list.
+ void Close();
+
+ private:
+ // Initializes the window.
+ void Init(const gfx::Rect& bounds,
+ const ShellDelegate::SetWidgetCallback& callback);
+
+ // Overridden from views::View:
+ virtual bool OnKeyPressed(const views::KeyEvent& event) OVERRIDE;
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE;
+
+ // Overridden from AppListItemModelViewListener:
+ virtual void AppListItemActivated(AppListItemView* sender,
+ int event_flags) OVERRIDE;
+
+ scoped_ptr<AppListModel> model_;
+
+ scoped_ptr<AppListViewDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(AppListView);
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_VIEW_H_
diff --git a/ui/aura_shell/app_list/app_list_view_delegate.h b/ui/aura_shell/app_list/app_list_view_delegate.h
new file mode 100644
index 0000000..a73b783
--- /dev/null
+++ b/ui/aura_shell/app_list/app_list_view_delegate.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_APP_LIST_VIEW_DELEGATE_H_
+#define UI_AURA_SHELL_APP_LIST_APP_LIST_VIEW_DELEGATE_H_
+#pragma once
+
+#include "ui/aura_shell/aura_shell_export.h"
+
+namespace aura_shell {
+
+class AppListItemModel;
+
+class AURA_SHELL_EXPORT AppListViewDelegate {
+ public:
+ // AppListView owns the delegate.
+ virtual ~AppListViewDelegate() {}
+
+ // Invoked an AppListeItemModelView is activated by click or enter key.
+ virtual void OnAppListItemActivated(AppListItemModel* item,
+ int event_flags) = 0;
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_APP_LIST_VIEW_DELEGATE_H_
diff --git a/ui/aura_shell/app_list/drop_shadow_label.cc b/ui/aura_shell/app_list/drop_shadow_label.cc
new file mode 100644
index 0000000..5b85224
--- /dev/null
+++ b/ui/aura_shell/app_list/drop_shadow_label.cc
@@ -0,0 +1,118 @@
+// Copyright (c) 2011 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/aura_shell/app_list/drop_shadow_label.h"
+
+#include "base/utf_string_conversions.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+#include "ui/gfx/canvas_skia.h"
+#include "ui/gfx/color_utils.h"
+#include "ui/gfx/skbitmap_operations.h"
+
+using views::Label;
+
+namespace aura_shell {
+
+static const int kDefaultDropShadowSize = 2;
+
+DropShadowLabel::DropShadowLabel() : drop_shadow_size_(kDefaultDropShadowSize) {
+}
+
+void DropShadowLabel::SetDropShadowSize(int drop_shadow_size) {
+ if (drop_shadow_size != drop_shadow_size_) {
+ drop_shadow_size_ = drop_shadow_size;
+ invalidate_text_size();
+ SchedulePaint();
+ }
+}
+
+void DropShadowLabel::PaintText(gfx::Canvas* canvas,
+ const string16& text,
+ const gfx::Rect& text_bounds,
+ int flags) {
+ SkColor text_color = enabled() ? enabled_color() : disabled_color();
+ if (drop_shadow_size_ > 0) {
+ // To properly render shadow with elliding fade effect, text and shadow
+ // is rendered to this canvas first with elliding disable so that underlying
+ // code would not mix shadow color into text area because of elliding fade.
+ // When that is done and if we need elliding fade, an alpha mask is applied
+ // when transfering contents on this canvas to target canvas.
+ gfx::Size canvas_size(text_bounds.width() + drop_shadow_size_,
+ text_bounds.height() + drop_shadow_size_);
+ gfx::CanvasSkia text_canvas(canvas_size, false);
+
+ const double kShadowOpacity = 0.2;
+ const SkColor shadow_color =
+ SkColorSetA(SK_ColorBLACK, kShadowOpacity * SkColorGetA(text_color));
+ gfx::Size text_size = GetTextSize();
+ for (int i = 0; i < drop_shadow_size_; i++) {
+ text_canvas.DrawStringInt(text, font(), shadow_color, i, 0,
+ text_size.width(), text_size.height(),
+ flags | gfx::Canvas::NO_ELLIPSIS);
+ text_canvas.DrawStringInt(text, font(), shadow_color, i, i,
+ text_size.width(), text_size.height(),
+ flags | gfx::Canvas::NO_ELLIPSIS);
+ text_canvas.DrawStringInt(text, font(), shadow_color, 0, i,
+ text_size.width(), text_size.height(),
+ flags | gfx::Canvas::NO_ELLIPSIS);
+ }
+ text_canvas.DrawStringInt(text, font(), text_color, 0, 0,
+ text_size.width(), text_size.height(),
+ flags | gfx::Canvas::NO_ELLIPSIS);
+
+ const SkBitmap& text_bitmap = const_cast<SkBitmap&>(
+ skia::GetTopDevice(*text_canvas.sk_canvas())->accessBitmap(false));
+
+ if (text_size.width() > text_bounds.width() &&
+ !(flags & gfx::Canvas::NO_ELLIPSIS)) {
+ // Apply an gradient alpha mask for elliding fade effect.
+ const double kFadeWidthFactor = 1.5;
+ int fade_width = std::min(text_size.width() / 2,
+ static_cast<int>(text_size.height() * kFadeWidthFactor));
+
+ const SkColor kColors[] = { SK_ColorWHITE, 0 };
+ const SkScalar kPoints[] = { SkIntToScalar(0), SkIntToScalar(1) };
+ SkPoint p[2];
+ p[0].set(SkIntToScalar(text_bounds.width() - fade_width),
+ SkIntToScalar(0));
+ p[1].set(SkIntToScalar(text_bounds.width()),
+ SkIntToScalar(0));
+ SkShader* s = SkGradientShader::CreateLinear(
+ p, kColors, kPoints, 2, SkShader::kClamp_TileMode, NULL);
+
+ SkPaint paint;
+ paint.setShader(s)->unref();
+
+ gfx::CanvasSkia alpha_canvas(canvas_size, false);
+ alpha_canvas.DrawRect(gfx::Rect(canvas_size), paint);
+
+ const SkBitmap& alpha_bitmap = const_cast<SkBitmap&>(
+ skia::GetTopDevice(*alpha_canvas.sk_canvas())->accessBitmap(false));
+ SkBitmap blended = SkBitmapOperations::CreateMaskedBitmap(text_bitmap,
+ alpha_bitmap);
+ canvas->DrawBitmapInt(blended, text_bounds.x(), text_bounds.y());
+ } else {
+ canvas->DrawBitmapInt(text_bitmap, text_bounds.x(), text_bounds.y());
+ }
+ } else {
+ canvas->DrawStringInt(text, font(), text_color, text_bounds.x(),
+ text_bounds.y(), text_bounds.width(), text_bounds.height(), flags);
+ }
+
+ if (HasFocus() || paint_as_focused()) {
+ gfx::Rect focus_bounds = text_bounds;
+ focus_bounds.Inset(-Label::kFocusBorderPadding,
+ -Label::kFocusBorderPadding);
+ canvas->DrawFocusRect(focus_bounds);
+ }
+}
+
+gfx::Size DropShadowLabel::GetTextSize() const {
+ gfx::Size text_size = Label::GetTextSize();
+ text_size.SetSize(text_size.width() + drop_shadow_size_,
+ text_size.height() + drop_shadow_size_);
+ return text_size;
+}
+
+} // namespace aura_shell
diff --git a/ui/aura_shell/app_list/drop_shadow_label.h b/ui/aura_shell/app_list/drop_shadow_label.h
new file mode 100644
index 0000000..ded5e31
--- /dev/null
+++ b/ui/aura_shell/app_list/drop_shadow_label.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011 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_AURA_SHELL_APP_LIST_DROP_SHADOW_LABEL_H_
+#define UI_AURA_SHELL_APP_LIST_DROP_SHADOW_LABEL_H_
+#pragma once
+
+#include "ui/gfx/font.h"
+#include "ui/views/controls/label.h"
+
+namespace aura_shell {
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DropShadowLabel class
+//
+// A drop shadow label is a view subclass that can display a string
+// with a drop shadow.
+//
+/////////////////////////////////////////////////////////////////////////////
+class DropShadowLabel : public views::Label {
+ public:
+ DropShadowLabel();
+
+ // Sets the size of the drop shadow drawn under the text.
+ // Defaults to two. Note that this is a really simplistic drop
+ // shadow -- it gets more expensive to draw the larger it gets,
+ // since it simply draws more copies of the string. For instance,
+ // for a value of two, the string is draw seven times. In general,
+ // it is drawn three extra times for each increment of |size|.
+ void SetDropShadowSize(int size);
+
+ // Return the size of the drop shadow in pixels.
+ int drop_shadow_size() const { return drop_shadow_size_; }
+
+ // Overridden to paint the text differently from the base class.
+ virtual void PaintText(gfx::Canvas* canvas,
+ const string16& text,
+ const gfx::Rect& text_bounds,
+ int flags) OVERRIDE;
+
+ protected:
+ virtual gfx::Size GetTextSize() const OVERRIDE;
+
+ private:
+ int drop_shadow_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DropShadowLabel);
+};
+
+} // namespace aura_shell
+
+#endif // UI_AURA_SHELL_APP_LIST_DROP_SHADOW_LABEL_H_
diff --git a/ui/aura_shell/aura_shell.gyp b/ui/aura_shell/aura_shell.gyp
index 8d23b0c..567e93d 100644
--- a/ui/aura_shell/aura_shell.gyp
+++ b/ui/aura_shell/aura_shell.gyp
@@ -37,10 +37,29 @@
'activation_controller.h',
'always_on_top_controller.cc',
'always_on_top_controller.h',
- 'app_list.cc',
- 'app_list.h',
'aura_shell_switches.cc',
'aura_shell_switches.h',
+ 'app_list/app_list.cc',
+ 'app_list/app_list.h',
+ 'app_list/app_list_groups_view.cc',
+ 'app_list/app_list_groups_view.h',
+ 'app_list/app_list_item_group_model.cc',
+ 'app_list/app_list_item_group_model.h',
+ 'app_list/app_list_item_group_view.cc',
+ 'app_list/app_list_item_group_view.h',
+ 'app_list/app_list_item_model.cc',
+ 'app_list/app_list_item_model.h',
+ 'app_list/app_list_item_model_observer.h',
+ 'app_list/app_list_item_view.cc',
+ 'app_list/app_list_item_view.h',
+ 'app_list/app_list_item_view_listener.h',
+ 'app_list/app_list_model.cc',
+ 'app_list/app_list_model.h',
+ 'app_list/app_list_view.cc',
+ 'app_list/app_list_view.h',
+ 'app_list/app_list_view_delegate.h',
+ 'app_list/drop_shadow_label.cc',
+ 'app_list/drop_shadow_label.h',
'compact_layout_manager.cc',
'compact_layout_manager.h',
'compact_status_area_layout_manager.cc',
@@ -222,6 +241,7 @@
'aura_shell',
],
'sources': [
+ 'examples/app_list.cc',
'examples/aura_shell_main.cc',
'examples/bubble.cc',
'examples/example_factory.h',
diff --git a/ui/aura_shell/aura_shell_switches.cc b/ui/aura_shell/aura_shell_switches.cc
index aee6bf3..6c6e728 100644
--- a/ui/aura_shell/aura_shell_switches.cc
+++ b/ui/aura_shell/aura_shell_switches.cc
@@ -17,6 +17,9 @@ const char kAuraNoShadows[] = "aura-no-shadows";
// Use Aura-style translucent window frame.
const char kAuraTranslucentFrames[] = "aura-translucent-frames";
+// Use views-based app list.
+const char kAuraViewsAppList[] = "aura-views-applist";
+
// Use a custom window style, e.g. --aura-window-mode=compact.
// When this flag is not passed we default to "normal" mode.
const char kAuraWindowMode[] = "aura-window-mode";
diff --git a/ui/aura_shell/aura_shell_switches.h b/ui/aura_shell/aura_shell_switches.h
index 95adef5..fd6ffcb3 100644
--- a/ui/aura_shell/aura_shell_switches.h
+++ b/ui/aura_shell/aura_shell_switches.h
@@ -14,6 +14,7 @@ namespace switches {
// Please keep alphabetized.
AURA_SHELL_EXPORT extern const char kAuraNoShadows[];
AURA_SHELL_EXPORT extern const char kAuraTranslucentFrames[];
+AURA_SHELL_EXPORT extern const char kAuraViewsAppList[];
AURA_SHELL_EXPORT extern const char kAuraWindowMode[];
AURA_SHELL_EXPORT extern const char kAuraWindowModeCompact[];
AURA_SHELL_EXPORT extern const char kAuraWindowModeNormal[];
diff --git a/ui/aura_shell/examples/app_list.cc b/ui/aura_shell/examples/app_list.cc
new file mode 100644
index 0000000..92b1fc1
--- /dev/null
+++ b/ui/aura_shell/examples/app_list.cc
@@ -0,0 +1,163 @@
+// Copyright (c) 2011 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 "base/basictypes.h"
+#include "ui/aura_shell/app_list/app_list_item_group_model.h"
+#include "ui/aura_shell/app_list/app_list_item_model.h"
+#include "ui/aura_shell/app_list/app_list_item_view.h"
+#include "ui/aura_shell/app_list/app_list_model.h"
+#include "ui/aura_shell/app_list/app_list_view_delegate.h"
+#include "ui/aura_shell/app_list/app_list_view.h"
+#include "ui/aura_shell/examples/example_factory.h"
+#include "ui/aura_shell/examples/toplevel_window.h"
+#include "ui/views/examples/examples_window.h"
+
+namespace aura_shell {
+namespace examples {
+
+namespace {
+
+class WindowTypeLauncherItem : public AppListItemModel {
+ public:
+ enum Type {
+ TOPLEVEL_WINDOW = 0,
+ NON_RESIZABLE_WINDOW,
+ LOCK_SCREEN,
+ WIDGETS_WINDOW,
+ EXAMPLES_WINDOW,
+ LAST_TYPE,
+ };
+
+ WindowTypeLauncherItem(Type type) : type_(type) {
+ SetIcon(GetIcon(type));
+ SetTitle(GetTitle(type));
+ }
+
+ static SkBitmap GetIcon(Type type) {
+ static const SkColor kColors[] = {
+ SkColorSetA(SK_ColorRED, 0x4F),
+ SkColorSetA(SK_ColorGREEN, 0x4F),
+ SkColorSetA(SK_ColorBLUE, 0x4F),
+ SkColorSetA(SK_ColorYELLOW, 0x4F),
+ SkColorSetA(SK_ColorCYAN, 0x4F),
+ };
+
+ SkBitmap icon;
+ icon.setConfig(SkBitmap::kARGB_8888_Config,
+ AppListItemView::kIconSize,
+ AppListItemView::kIconSize);
+ icon.allocPixels();
+ icon.eraseColor(kColors[static_cast<int>(type) % arraysize(kColors)]);
+ return icon;
+ }
+
+ static std::string GetTitle(Type type) {
+ switch (type) {
+ case TOPLEVEL_WINDOW:
+ return "Create Window";
+ case NON_RESIZABLE_WINDOW:
+ return "Create Non-Resizable Window";
+ case LOCK_SCREEN:
+ return "Lock Screen";
+ case WIDGETS_WINDOW:
+ return "Show Example Widgets";
+ case EXAMPLES_WINDOW:
+ return "Open Views Examples Window";
+ default:
+ return "Unknown window type.";
+ }
+ }
+
+ void Activate(int event_flags) {
+ switch (type_) {
+ case TOPLEVEL_WINDOW: {
+ ToplevelWindow::CreateParams params;
+ params.can_resize = true;
+ ToplevelWindow::CreateToplevelWindow(params);
+ break;
+ }
+ case NON_RESIZABLE_WINDOW: {
+ ToplevelWindow::CreateToplevelWindow(ToplevelWindow::CreateParams());
+ break;
+ }
+ case LOCK_SCREEN: {
+ CreateLockScreen();
+ break;
+ }
+ case WIDGETS_WINDOW: {
+ CreateWidgetsWindow();
+ break;
+ }
+ case EXAMPLES_WINDOW: {
+ views::examples::ShowExamplesWindow(false);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ private:
+ Type type_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowTypeLauncherItem);
+};
+
+class ExampleAppListViewDelegate : public AppListViewDelegate {
+ public:
+ ExampleAppListViewDelegate() {}
+
+ private:
+ virtual void OnAppListItemActivated(AppListItemModel* item,
+ int event_flags) OVERRIDE {
+ static_cast<WindowTypeLauncherItem*>(item)->Activate(event_flags);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ExampleAppListViewDelegate);
+};
+
+AppListItemGroupModel* CreateGroup(const std::string& title,
+ WindowTypeLauncherItem::Type start_type,
+ WindowTypeLauncherItem::Type end_type) {
+ AppListItemGroupModel* group = new AppListItemGroupModel(title);
+ for (int i = static_cast<int>(start_type);
+ i < static_cast<int>(end_type);
+ ++i) {
+ WindowTypeLauncherItem::Type type =
+ static_cast<WindowTypeLauncherItem::Type>(i);
+ group->AddItem(new WindowTypeLauncherItem(type));
+ }
+ return group;
+}
+
+} // namespace
+
+void BuildAppListModel(AppListModel* model) {
+ model->AddGroup(CreateGroup("Windows",
+ WindowTypeLauncherItem::TOPLEVEL_WINDOW,
+ WindowTypeLauncherItem::WIDGETS_WINDOW));
+ model->AddGroup(CreateGroup("Samples",
+ WindowTypeLauncherItem::WIDGETS_WINDOW,
+ WindowTypeLauncherItem::LAST_TYPE));
+}
+
+AppListViewDelegate* CreateAppListViewDelegate() {
+ return new ExampleAppListViewDelegate;
+}
+
+// TODO(xiyuan): Remove this.
+void CreateAppList(const gfx::Rect& bounds,
+ const ShellDelegate::SetWidgetCallback& callback) {
+ AppListModel* model = new AppListModel();
+ BuildAppListModel(model);
+
+ new aura_shell::AppListView(
+ model,
+ CreateAppListViewDelegate(),
+ bounds,
+ callback);
+}
+
+} // namespace examples
+} // namespace aura_shell
diff --git a/ui/aura_shell/examples/aura_shell_main.cc b/ui/aura_shell/examples/aura_shell_main.cc
index 0700047..7fc9418 100644
--- a/ui/aura_shell/examples/aura_shell_main.cc
+++ b/ui/aura_shell/examples/aura_shell_main.cc
@@ -8,6 +8,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "ui/aura/root_window.h"
+#include "ui/aura_shell/examples/example_factory.h"
#include "ui/aura_shell/examples/toplevel_window.h"
#include "ui/aura_shell/launcher/launcher_types.h"
#include "ui/aura_shell/shell.h"
@@ -23,34 +24,6 @@
namespace {
-class AppListWindow : public views::WidgetDelegateView {
- public:
- AppListWindow() {
- }
-
- // static
- static views::Widget* Create(const gfx::Rect& bounds) {
- AppListWindow* app_list = new AppListWindow;
-
- views::Widget::InitParams widget_params(
- views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
- widget_params.bounds = bounds;
- widget_params.delegate = app_list;
- widget_params.keep_on_top = true;
- widget_params.transparent = true;
-
- views::Widget* widget = new views::Widget;
- widget->Init(widget_params);
- widget->SetContentsView(app_list);
- return widget;
- }
-
- // Overridden from views::View:
- virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {
- canvas->FillRect(SkColorSetARGB(0x4F, 0xFF, 0, 0), bounds());
- }
-};
-
class ShellDelegateImpl : public aura_shell::ShellDelegate {
public:
ShellDelegateImpl() {
@@ -70,7 +43,19 @@ class ShellDelegateImpl : public aura_shell::ShellDelegate {
virtual void RequestAppListWidget(
const gfx::Rect& bounds,
const SetWidgetCallback& callback) OVERRIDE {
- callback.Run(AppListWindow::Create(bounds));
+ // TODO(xiyuan): Clean this up.
+ // The code below here is because we don't want to use
+ // --aura-views-applist. This function is deprecated and all code
+ // here will be removed when we clean it up.
+ aura_shell::examples::CreateAppList(bounds, callback);
+ }
+
+ virtual void BuildAppListModel(aura_shell::AppListModel* model) {
+ aura_shell::examples::BuildAppListModel(model);
+ }
+
+ virtual aura_shell::AppListViewDelegate* CreateAppListViewDelegate() {
+ return aura_shell::examples::CreateAppListViewDelegate();
}
virtual void LauncherItemClicked(
diff --git a/ui/aura_shell/examples/example_factory.h b/ui/aura_shell/examples/example_factory.h
index d78dedb..b17b5ce 100644
--- a/ui/aura_shell/examples/example_factory.h
+++ b/ui/aura_shell/examples/example_factory.h
@@ -6,11 +6,17 @@
#define UI_AURA_SHELL_EXAMPLES_EXAMPLE_FACTORY_H_
#pragma once
+#include "ui/aura_shell/shell_delegate.h"
+
namespace views {
class View;
}
namespace aura_shell {
+
+class AppListModel;
+class AppListViewDelegate;
+
namespace examples {
void CreatePointyBubble(views::View* anchor_view);
@@ -20,6 +26,13 @@ void CreateLockScreen();
// Creates a window showing samples of commonly used widgets.
void CreateWidgetsWindow();
+void BuildAppListModel(AppListModel* model);
+
+AppListViewDelegate* CreateAppListViewDelegate();
+
+void CreateAppList(const gfx::Rect& bounds,
+ const ShellDelegate::SetWidgetCallback& callback);
+
} // namespace examples
} // namespace aura_shell
diff --git a/ui/aura_shell/shell.cc b/ui/aura_shell/shell.cc
index de891c8..116fef2 100644
--- a/ui/aura_shell/shell.cc
+++ b/ui/aura_shell/shell.cc
@@ -12,7 +12,7 @@
#include "ui/aura/layout_manager.h"
#include "ui/aura/window.h"
#include "ui/aura_shell/activation_controller.h"
-#include "ui/aura_shell/app_list.h"
+#include "ui/aura_shell/app_list/app_list.h"
#include "ui/aura_shell/aura_shell_switches.h"
#include "ui/aura_shell/compact_layout_manager.h"
#include "ui/aura_shell/compact_status_area_layout_manager.h"
diff --git a/ui/aura_shell/shell_delegate.h b/ui/aura_shell/shell_delegate.h
index 982ef8d..7912919 100644
--- a/ui/aura_shell/shell_delegate.h
+++ b/ui/aura_shell/shell_delegate.h
@@ -19,6 +19,8 @@ class Widget;
namespace aura_shell {
+class AppListModel;
+class AppListViewDelegate;
struct LauncherItem;
// Delegate of the Shell.
@@ -39,10 +41,19 @@ class AURA_SHELL_EXPORT ShellDelegate {
// Invoked to create app list widget. The Delegate calls the callback
// when the widget is ready to show.
+ // Deprecated.
+ // TODO(xiyuan): Clean this up when switching to views app list.
virtual void RequestAppListWidget(
const gfx::Rect& bounds,
const SetWidgetCallback& callback) = 0;
+ // Invoked to ask the delegate to populate the |model|.
+ virtual void BuildAppListModel(AppListModel* model) = 0;
+
+ // Invoked to create an AppListViewDelegate. Shell takes the ownership of
+ // the created delegate.
+ virtual AppListViewDelegate* CreateAppListViewDelegate() = 0;
+
// Invoked when the user clicks on a window entry in the launcher.
virtual void LauncherItemClicked(const LauncherItem& item) = 0;
diff --git a/ui/aura_shell/test/test_shell_delegate.cc b/ui/aura_shell/test/test_shell_delegate.cc
index 42414b7..0a58e7c 100644
--- a/ui/aura_shell/test/test_shell_delegate.cc
+++ b/ui/aura_shell/test/test_shell_delegate.cc
@@ -25,6 +25,13 @@ void TestShellDelegate::RequestAppListWidget(
const SetWidgetCallback& callback) {
}
+void TestShellDelegate::BuildAppListModel(AppListModel* model) {
+}
+
+AppListViewDelegate* TestShellDelegate::CreateAppListViewDelegate() {
+ return NULL;
+}
+
void TestShellDelegate::LauncherItemClicked(const LauncherItem& item) {
}
diff --git a/ui/aura_shell/test/test_shell_delegate.h b/ui/aura_shell/test/test_shell_delegate.h
index 6811d45..a300b00 100644
--- a/ui/aura_shell/test/test_shell_delegate.h
+++ b/ui/aura_shell/test/test_shell_delegate.h
@@ -23,6 +23,8 @@ class TestShellDelegate : public ShellDelegate {
virtual void RequestAppListWidget(
const gfx::Rect& bounds,
const SetWidgetCallback& callback) OVERRIDE;
+ virtual void BuildAppListModel(AppListModel* model) OVERRIDE;
+ virtual AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE;
virtual void LauncherItemClicked(const LauncherItem& item) OVERRIDE;
virtual bool ConfigureLauncherItem(LauncherItem* item) OVERRIDE;
};
diff --git a/ui/base/models/list_model.h b/ui/base/models/list_model.h
new file mode 100644
index 0000000..3634ed1
--- /dev/null
+++ b/ui/base/models/list_model.h
@@ -0,0 +1,116 @@
+// Copyright (c) 2011 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_BASE_MODELS_LIST_MODEL_H_
+#define UI_BASE_MODELS_LIST_MODEL_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/observer_list.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/base/models/list_model_observer.h"
+
+namespace ui {
+
+// A list model that manages a list of ItemType pointers. Items added to the
+// model are owned by the model. An item can be taken out of the model by
+// RemoveAt.
+template <class ItemType>
+class ListModel {
+ public:
+ typedef std::vector<ItemType*> Items;
+
+ ListModel() {}
+ virtual ~ListModel() {}
+
+ // Adds |item| to the model at given |index|.
+ virtual void AddAt(int index, ItemType* item) {
+ DCHECK(index >= 0 && index <= item_count());
+ items_->insert(items_.begin() + index, item);
+ NotifyItemsAdded(index, 1);
+ }
+
+ // Removes an item at given |index| from the model. Note the removed item
+ // is NOT deleted and it's up to the caller to delete it.
+ virtual ItemType* RemoveAt(int index) {
+ DCHECK(index >= 0 && index < item_count());
+ ItemType* item = items_[index];
+ items_->erase(items_.begin() + index);
+ NotifyItemsRemoved(index, 1);
+ return item;
+ }
+
+ // Removes all items from the model. This does NOT delete the items.
+ virtual void RemoveAll() {
+ int count = item_count();
+ items_->clear();
+ NotifyItemsRemoved(0, count);
+ }
+
+ // Removes an item at given |index| from the model and deletes it.
+ virtual void DeleteAt(int index) {
+ delete RemoveAt(index);
+ }
+
+ // Removes and deletes all items from the model.
+ virtual void DeleteAll() {
+ int count = item_count();
+ items_.reset();
+ NotifyItemsRemoved(0, count);
+ }
+
+ // Convenience function to append an item to the model.
+ void Add(ItemType* item) {
+ AddAt(item_count(), item);
+ }
+
+ void AddObserver(ListModelObserver* observer) {
+ observers_.AddObserver(observer);
+ }
+
+ void RemoveObserver(ListModelObserver* observer) {
+ observers_.RemoveObserver(observer);
+ }
+
+ void NotifyItemsAdded(int start, int count) {
+ FOR_EACH_OBSERVER(ListModelObserver,
+ observers_,
+ ListItemsAdded(start, count));
+ }
+
+ void NotifyItemsRemoved(int start, int count) {
+ FOR_EACH_OBSERVER(ListModelObserver,
+ observers_,
+ ListItemsRemoved(start, count));
+ }
+
+ void NotifyItemsChanged(int start, int count) {
+ FOR_EACH_OBSERVER(ListModelObserver,
+ observers_,
+ ListItemsChanged(start, count));
+ }
+
+ int item_count() const { return static_cast<int>(items_.size()); }
+ const Items& items() const { return items_.get(); }
+
+ const ItemType* item_at(int index) const {
+ DCHECK(index >= 0 && index < item_count());
+ return items_[index];
+ }
+ ItemType* item_at(int index) {
+ return const_cast<ItemType*>(
+ const_cast<const ListModel<ItemType>*>(this)->item_at(index));
+ }
+
+ private:
+ ScopedVector<ItemType> items_;
+ ObserverList<ListModelObserver> observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListModel<ItemType>);
+};
+
+} // namespace ui
+
+#endif // UI_BASE_MODELS_LIST_MODEL_H_
diff --git a/ui/base/models/list_model_observer.h b/ui/base/models/list_model_observer.h
new file mode 100644
index 0000000..65b22a8
--- /dev/null
+++ b/ui/base/models/list_model_observer.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 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_BASE_MODELS_LIST_MODEL_OBSERVER_H_
+#define UI_BASE_MODELS_LIST_MODEL_OBSERVER_H_
+#pragma once
+
+#include "ui/base/ui_export.h"
+
+namespace ui {
+
+class UI_EXPORT ListModelObserver {
+ public:
+ // Invoked after items has been added to the model.
+ virtual void ListItemsAdded(int start, int count) = 0;
+
+ // Invoked after items has been removed. |start| is the index before the
+ // removal.
+ virtual void ListItemsRemoved(int start, int count) = 0;
+
+ // Invoked after items has been changed.
+ virtual void ListItemsChanged(int start, int count) = 0;
+
+ protected:
+ virtual ~ListModelObserver() {}
+};
+
+} // namespace ui
+
+#endif // UI_BASE_MODELS_LIST_MODEL_OBSERVER_H_
diff --git a/ui/base/models/list_model_unittest.cc b/ui/base/models/list_model_unittest.cc
new file mode 100644
index 0000000..1718431
--- /dev/null
+++ b/ui/base/models/list_model_unittest.cc
@@ -0,0 +1,149 @@
+// Copyright (c) 2011 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/base/models/list_model.h"
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+class FooItem {
+ public:
+ explicit FooItem(int id) : id_(id) {}
+
+ int id() const { return id_; }
+
+ private:
+ int id_;
+ DISALLOW_COPY_AND_ASSIGN(FooItem);
+};
+
+class ListModelTest : public testing::Test,
+ public ListModelObserver {
+ public:
+ ListModelTest()
+ : added_count_(0),
+ removed_count_(0),
+ changed_count_(0) {
+ }
+
+ void ExpectCountsEqual(int added_count,
+ int removed_count,
+ int changed_count) {
+ EXPECT_EQ(added_count, added_count_);
+ EXPECT_EQ(removed_count, removed_count_);
+ EXPECT_EQ(changed_count, changed_count_);
+ }
+
+ void ClearCounts() {
+ added_count_ = removed_count_ = changed_count_ = 0;
+ }
+
+ // ListModelObserver implementation:
+ virtual void ListItemsAdded(int start, int count) OVERRIDE {
+ added_count_ += count;
+ }
+ virtual void ListItemsRemoved(int start, int count) OVERRIDE {
+ removed_count_ += count;
+ }
+ virtual void ListItemsChanged(int start, int count) OVERRIDE {
+ changed_count_ += count;
+ }
+
+ private:
+ int added_count_;
+ int removed_count_;
+ int changed_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(ListModelTest);
+};
+
+TEST_F(ListModelTest, Add) {
+ ListModel<FooItem> model;
+ model.AddObserver(this);
+
+ // Append FooItem(0)
+ model.Add(new FooItem(0));
+ ExpectCountsEqual(1, 0, 0);
+
+ // Append FooItem(1)
+ model.Add(new FooItem(1));
+ ExpectCountsEqual(2, 0, 0);
+
+ // Insert FooItem(2) at position 0
+ model.AddAt(0, new FooItem(2));
+ ExpectCountsEqual(3, 0, 0);
+
+ // Total 3 items in mode.
+ EXPECT_EQ(3, model.item_count());
+
+ // First one should be FooItem(2), followed by FooItem(0) and FooItem(1)
+ EXPECT_EQ(2, model.item_at(0)->id());
+ EXPECT_EQ(0, model.item_at(1)->id());
+ EXPECT_EQ(1, model.item_at(2)->id());
+}
+
+TEST_F(ListModelTest, Remove) {
+ ListModel<FooItem> model;
+ model.AddObserver(this);
+
+ model.Add(new FooItem(0));
+ model.Add(new FooItem(1));
+ model.Add(new FooItem(2));
+
+ ClearCounts();
+
+ // Remove item at index 1 from model and release memory.
+ model.DeleteAt(1);
+ ExpectCountsEqual(0, 1, 0);
+
+ EXPECT_EQ(2, model.item_count());
+ EXPECT_EQ(0, model.item_at(0)->id());
+ EXPECT_EQ(2, model.item_at(1)->id());
+
+ // Remove all items from model and delete them.
+ model.DeleteAll();
+ ExpectCountsEqual(0, 3, 0);
+}
+
+TEST_F(ListModelTest, RemoveAll) {
+ ListModel<FooItem> model;
+ model.AddObserver(this);
+
+ scoped_ptr<FooItem> foo0(new FooItem(0));
+ scoped_ptr<FooItem> foo1(new FooItem(1));
+ scoped_ptr<FooItem> foo2(new FooItem(2));
+
+ model.Add(foo0.get());
+ model.Add(foo1.get());
+ model.Add(foo2.get());
+
+ ClearCounts();
+
+ // Remove all items and scoped_ptr above would release memory.
+ model.RemoveAll();
+ ExpectCountsEqual(0, 3, 0);
+}
+
+TEST_F(ListModelTest, FakeUpdate) {
+ ListModel<FooItem> model;
+ model.AddObserver(this);
+
+ model.Add(new FooItem(0));
+ model.Add(new FooItem(1));
+ model.Add(new FooItem(2));
+
+ ClearCounts();
+
+ model.NotifyItemsChanged(0, 1);
+ ExpectCountsEqual(0, 0, 1);
+
+ model.NotifyItemsChanged(1, 2);
+ ExpectCountsEqual(0, 0, 3);
+}
+
+} // namespace ui
diff --git a/ui/ui.gyp b/ui/ui.gyp
index d49d141..4b50684 100644
--- a/ui/ui.gyp
+++ b/ui/ui.gyp
@@ -174,6 +174,8 @@
'base/models/button_menu_item_model.cc',
'base/models/button_menu_item_model.h',
'base/models/combobox_model.h',
+ 'base/models/list_model.h',
+ 'base/models/list_model_observer.h',
'base/models/menu_model.cc',
'base/models/menu_model.h',
'base/models/menu_model_delegate.h',
diff --git a/ui/ui_unittests.gypi b/ui/ui_unittests.gypi
index ced2aa3..6130da5 100644
--- a/ui/ui_unittests.gypi
+++ b/ui/ui_unittests.gypi
@@ -57,6 +57,7 @@
'base/ime/character_composer_unittest.cc',
'base/l10n/l10n_util_mac_unittest.mm',
'base/l10n/l10n_util_unittest.cc',
+ 'base/models/list_model_unittest.cc',
'base/models/tree_node_iterator_unittest.cc',
'base/models/tree_node_model_unittest.cc',
'base/range/range_unittest.cc',