diff options
author | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-22 11:16:58 +0000 |
---|---|---|
committer | xiyuan@chromium.org <xiyuan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-12-22 11:16:58 +0000 |
commit | 61af999511b96b3a9adffdb657f312771e8658e3 (patch) | |
tree | 8c27d4fb8ec795c887549f33d73b6531af0a1f51 /ui | |
parent | c649d52d0c673eae23af01f266ae8c657d8e8fd7 (diff) | |
download | chromium_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')
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, ¤t_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 @@ -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', |