diff options
61 files changed, 2622 insertions, 115 deletions
diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc new file mode 100644 index 0000000..b8022ab --- /dev/null +++ b/ash/shell/app_list.cc @@ -0,0 +1,166 @@ +// 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 "ash/shell/example_factory.h" +#include "ash/shell/toplevel_window.h" +#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/views/examples/examples_window.h" + +namespace ash { +namespace shell { + +namespace { + +class WindowTypeLauncherItem : public aura_shell::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, + aura_shell::AppListItemView::kIconSize, + aura_shell::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 aura_shell::AppListViewDelegate { + public: + ExampleAppListViewDelegate() {} + + private: + virtual void OnAppListItemActivated(aura_shell::AppListItemModel* item, + int event_flags) OVERRIDE { + static_cast<WindowTypeLauncherItem*>(item)->Activate(event_flags); + } + + DISALLOW_COPY_AND_ASSIGN(ExampleAppListViewDelegate); +}; + +aura_shell::AppListItemGroupModel* CreateGroup( + const std::string& title, + WindowTypeLauncherItem::Type start_type, + WindowTypeLauncherItem::Type end_type) { + aura_shell::AppListItemGroupModel* group = + new aura_shell::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(aura_shell::AppListModel* model) { + model->AddGroup(CreateGroup("Windows", + WindowTypeLauncherItem::TOPLEVEL_WINDOW, + WindowTypeLauncherItem::WIDGETS_WINDOW)); + model->AddGroup(CreateGroup("Samples", + WindowTypeLauncherItem::WIDGETS_WINDOW, + WindowTypeLauncherItem::LAST_TYPE)); +} + +aura_shell::AppListViewDelegate* CreateAppListViewDelegate() { + return new ExampleAppListViewDelegate; +} + +// TODO(xiyuan): Remove this. +void CreateAppList( + const gfx::Rect& bounds, + const aura_shell::ShellDelegate::SetWidgetCallback& callback) { + aura_shell::AppListModel* model = new aura_shell::AppListModel; + BuildAppListModel(model); + + new aura_shell::AppListView( + model, + CreateAppListViewDelegate(), + bounds, + callback); +} + +} // namespace shell +} // namespace ash diff --git a/ash/shell/example_factory.h b/ash/shell/example_factory.h index 25491ac..76e1f6e 100644 --- a/ash/shell/example_factory.h +++ b/ash/shell/example_factory.h @@ -6,6 +6,13 @@ #define ASH_SHELL_EXAMPLE_FACTORY_H_ #pragma once +#include "ui/aura_shell/shell_delegate.h" + +namespace aura_shell { +class AppListModel; +class AppListViewDelegate; +} + namespace views { class View; } @@ -20,6 +27,14 @@ void CreateLockScreen(); // Creates a window showing samples of commonly used widgets. void CreateWidgetsWindow(); +void BuildAppListModel(aura_shell::AppListModel* model); + +aura_shell::AppListViewDelegate* CreateAppListViewDelegate(); + +void CreateAppList( + const gfx::Rect& bounds, + const aura_shell::ShellDelegate::SetWidgetCallback& callback); + } // namespace shell } // namespace ash diff --git a/ash/shell/shell_main.cc b/ash/shell/shell_main.cc index d63a9c7..0bde60d 100644 --- a/ash/shell/shell_main.cc +++ b/ash/shell/shell_main.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ash/shell/example_factory.h" #include "ash/shell/toplevel_window.h" #include "base/at_exit.h" #include "base/command_line.h" @@ -24,34 +25,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() { @@ -71,7 +44,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. + ash::shell::CreateAppList(bounds, callback); + } + + virtual void BuildAppListModel(aura_shell::AppListModel* model) { + ash::shell::BuildAppListModel(model); + } + + virtual aura_shell::AppListViewDelegate* CreateAppListViewDelegate() { + return ash::shell::CreateAppListViewDelegate(); } virtual void LauncherItemClicked( diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 4c4db72..0570436 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -14822,6 +14822,12 @@ Battery full <message name="IDS_SEARCH_BOX_HINT" desc="Hint text for the search box in app list window."> Search apps and more </message> + <message name="IDS_APP_LIST_INCOGNITO" desc="Title text for an app list item that opens an incognito browser window."> + Incognito + </message> + <message name="IDS_APP_LIST_SETTINGS" desc="Title text for an app list item that opens settings page."> + Settings + </message> </if> </messages> diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd index bbdac5f..d645f62 100644 --- a/chrome/app/theme/theme_resources.grd +++ b/chrome/app/theme/theme_resources.grd @@ -573,6 +573,12 @@ <include name="IDR_VOLUME_BUBBLE_DOWN_ICON" file="volume_down_icon.png" type="BINDATA" /> <include name="IDR_VOLUME_BUBBLE_MUTE_ICON" file="volume_mute_icon.png" type="BINDATA" /> </if> + + <!-- Images only used by aura. --> + <if expr="pp_ifdef('use_aura')"> + <include name="IDR_APP_LIST_INCOGNITO" file="app_list_incognito.png" type="BINDATA" /> + <include name="IDR_APP_LIST_SETTINGS" file="app_list_settings.png" type="BINDATA" /> + </if> </includes> </release> </grit> diff --git a/chrome/browser/chromeos/drop_shadow_label.cc b/chrome/browser/chromeos/drop_shadow_label.cc deleted file mode 100644 index 54243ec..0000000 --- a/chrome/browser/chromeos/drop_shadow_label.cc +++ /dev/null @@ -1,68 +0,0 @@ -// 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 "chrome/browser/chromeos/drop_shadow_label.h" - -#include "base/utf_string_conversions.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/color_utils.h" - -using views::Label; - -namespace chromeos { - -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) { - const float kShadowOpacity = 0.2; - const SkColor shadow_color = - SkColorSetA(SK_ColorBLACK, kShadowOpacity * SkColorGetA(text_color)); - for (int i = 0; i < drop_shadow_size_; i++) { - canvas->DrawStringInt(text, font(), shadow_color, - text_bounds.x() + i, text_bounds.y(), - text_bounds.width(), text_bounds.height(), flags); - canvas->DrawStringInt(text, font(), shadow_color, - text_bounds.x() + i, text_bounds.y() + i, - text_bounds.width(), text_bounds.height(), flags); - canvas->DrawStringInt(text, font(), shadow_color, - text_bounds.x(), text_bounds.y() + i, - text_bounds.width(), text_bounds.height(), flags); - } - } - - 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 chromeos diff --git a/chrome/browser/ui/views/aura/app_list/app_list_model_builder.cc b/chrome/browser/ui/views/aura/app_list/app_list_model_builder.cc new file mode 100644 index 0000000..f35dbb9 --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/app_list_model_builder.cc @@ -0,0 +1,122 @@ +// 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 "chrome/browser/ui/views/aura/app_list/app_list_model_builder.h" + +#include "chrome/app/chrome_command_ids.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_list.h" +#include "chrome/browser/ui/views/aura/app_list/browser_command_item.h" +#include "chrome/browser/ui/views/aura/app_list/extension_app_item.h" +#include "chrome/browser/ui/webui/ntp/app_launcher_handler.h" +#include "chrome/common/pref_names.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" +#include "ui/aura_shell/app_list/app_list_item_group_model.h" +#include "ui/base/l10n/l10n_util.h" + +namespace { + +// Gets or creates group for given |extension| in |model|. The created group +// is added to |model| and owned by it. +aura_shell::AppListItemGroupModel* GetOrCreateGroup( + int page_index, + const ListValue* app_page_names, + aura_shell::AppListModel* model) { + if (page_index >= model->group_count()) { + for (int i = model->group_count(); i <= page_index; ++i) { + std::string title; + if (!app_page_names->GetString(i, &title)) + title = l10n_util::GetStringUTF8(IDS_APP_DEFAULT_PAGE_NAME); + + aura_shell::AppListItemGroupModel* group = + new aura_shell::AppListItemGroupModel(title); + model->AddGroup(group); + } + } + + return model->GetGroup(page_index); +} + +// Binary predict to sort extension apps. Smaller launch ordinal takes +// precedence. +bool ExtensionAppPrecedes(const ExtensionAppItem* a, + const ExtensionAppItem* b) { + return a->launch_ordinal().LessThan(b->launch_ordinal()); +} + +} // namespace + +AppListModelBuilder::AppListModelBuilder(Profile* profile, + aura_shell::AppListModel* model) + : profile_(profile), + model_(model) { +} + +AppListModelBuilder::~AppListModelBuilder() { +} + +void AppListModelBuilder::Build() { + GetExtensionApps(); + GetBrowserCommands(); +} + +void AppListModelBuilder::GetExtensionApps() { + DCHECK(profile_); + ExtensionService* service = profile_->GetExtensionService(); + if (!service) + return; + + // Get extension apps. + std::vector<ExtensionAppItem*> items; + const ExtensionSet* extensions = service->extensions(); + for (ExtensionSet::const_iterator app = extensions->begin(); + app != extensions->end(); ++app) { + if (AppLauncherHandler::IsAppExcludedFromList(*app)) + continue; + + items.push_back(new ExtensionAppItem(profile_, *app)); + } + + // Sort by launch ordinal. + std::sort(items.begin(), items.end(), &ExtensionAppPrecedes); + + // Put all items into model and group them by page ordinal. + PrefService* prefs = profile_->GetPrefs(); + const ListValue* app_page_names = prefs->GetList(prefs::kNTPAppPageNames); + for (size_t i = 0; i < items.size(); ++i) { + ExtensionAppItem* item = items[i]; + + aura_shell::AppListItemGroupModel* group = GetOrCreateGroup( + item->page_index(), + app_page_names, + model_); + + group->AddItem(item); + } +} + +void AppListModelBuilder::GetBrowserCommands() { + Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); + if (!browser) + return; + + // Uses the first group to put browser commands + if (model_->group_count() == 0) + model_->AddGroup(new aura_shell::AppListItemGroupModel("")); + aura_shell::AppListItemGroupModel* group = model_->GetGroup(0); + + group->AddItem(new BrowserCommandItem(browser, + IDC_NEW_INCOGNITO_WINDOW, + IDS_APP_LIST_INCOGNITO, + IDR_APP_LIST_INCOGNITO)); + group->AddItem(new BrowserCommandItem(browser, + IDC_OPTIONS, + IDS_APP_LIST_SETTINGS, + IDR_APP_LIST_SETTINGS)); +} + diff --git a/chrome/browser/ui/views/aura/app_list/app_list_model_builder.h b/chrome/browser/ui/views/aura/app_list/app_list_model_builder.h new file mode 100644 index 0000000..b42182539 --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/app_list_model_builder.h @@ -0,0 +1,34 @@ +// 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 CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_APP_LIST_MODEL_BUILDER_H_ +#define CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_APP_LIST_MODEL_BUILDER_H_ +#pragma once + +#include "base/gtest_prod_util.h" +#include "ui/aura_shell/app_list/app_list_model.h" + +class Profile; + +class AppListModelBuilder { + public: + AppListModelBuilder(Profile* profile, aura_shell::AppListModel* model); + virtual ~AppListModelBuilder(); + + // Populates the model. + void Build(); + + private: + FRIEND_TEST_ALL_PREFIXES(AppListModelBuilderTest, GetExtensionApps); + + void GetExtensionApps(); + void GetBrowserCommands(); + + Profile* profile_; + aura_shell::AppListModel* model_; + + DISALLOW_COPY_AND_ASSIGN(AppListModelBuilder); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_APP_LIST_MODEL_BUILDER_H_ diff --git a/chrome/browser/ui/views/aura/app_list/app_list_model_builder_unittest.cc b/chrome/browser/ui/views/aura/app_list/app_list_model_builder_unittest.cc new file mode 100644 index 0000000..4ff8338 --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/app_list_model_builder_unittest.cc @@ -0,0 +1,50 @@ +// 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/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/ui/views/aura/app_list/app_list_model_builder.h" +#include "chrome/browser/extensions/extension_service_unittest.h" +#include "chrome/test/base/testing_profile.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/aura_shell/app_list/app_list_item_group_model.h" +#include "ui/aura_shell/app_list/app_list_item_model.h" + +class AppListModelBuilderTest : public ExtensionServiceTestBase { + public: + AppListModelBuilderTest() {} + virtual ~AppListModelBuilderTest() {} +}; + +TEST_F(AppListModelBuilderTest, GetExtensionApps) { + // Load "app_list" extensions test profile. The test profile has 4 extensions: + // 1 dummy extension, 2 packaged extension apps and 1 hosted extension app. + FilePath source_install_dir = data_dir_ + .AppendASCII("app_list") + .AppendASCII("Extensions"); + FilePath pref_path = source_install_dir + .DirName() + .AppendASCII("Preferences"); + InitializeInstalledExtensionService(pref_path, source_install_dir); + service_->Init(); + + // There should be 4 extensions in the test profile. + const ExtensionSet* extensions = service_->extensions(); + ASSERT_EQ(static_cast<size_t>(4), extensions->size()); + + scoped_ptr<aura_shell::AppListModel> model(new aura_shell::AppListModel()); + AppListModelBuilder builder(profile_.get(), model.get()); + builder.GetExtensionApps(); + + // Expect to have two app groups. + EXPECT_EQ(2, model->group_count()); + + // Two packaged apps are on the first page and hosted app on the 2nd page. + aura_shell::AppListItemGroupModel* group1 = model->GetGroup(0); + EXPECT_EQ("Packaged App 1", group1->GetItem(0)->title()); + EXPECT_EQ("Packaged App 2", group1->GetItem(1)->title()); + + aura_shell::AppListItemGroupModel* group2 = model->GetGroup(1); + EXPECT_EQ("Hosted App", group2->GetItem(0)->title()); +} diff --git a/chrome/browser/ui/views/aura/app_list/app_list_view_delegate.cc b/chrome/browser/ui/views/aura/app_list/app_list_view_delegate.cc new file mode 100644 index 0000000..c624778 --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/app_list_view_delegate.cc @@ -0,0 +1,19 @@ +// 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 "chrome/browser/ui/views/aura/app_list/app_list_view_delegate.h" + +#include "chrome/browser/ui/views/aura/app_list/chrome_app_list_item.h" + +AppListViewDelegate::AppListViewDelegate() { +} + +AppListViewDelegate::~AppListViewDelegate() { +} + +void AppListViewDelegate::OnAppListItemActivated( + aura_shell::AppListItemModel* item, + int event_flags) { + static_cast<ChromeAppListItem*>(item)->Activate(event_flags); +} diff --git a/chrome/browser/ui/views/aura/app_list/app_list_view_delegate.h b/chrome/browser/ui/views/aura/app_list/app_list_view_delegate.h new file mode 100644 index 0000000..1f185f3 --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/app_list_view_delegate.h @@ -0,0 +1,26 @@ +// 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 CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_APP_LIST_VIEW_DELEGATE_H_ +#define CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_APP_LIST_VIEW_DELEGATE_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "ui/aura_shell/app_list/app_list_view_delegate.h" + +class AppListViewDelegate : public aura_shell::AppListViewDelegate { + public: + AppListViewDelegate(); + virtual ~AppListViewDelegate(); + + private: + // Overridden from aura_shell::AppListViewDelegate: + virtual void OnAppListItemActivated(aura_shell::AppListItemModel* item, + int event_flags) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(AppListViewDelegate); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_APP_LIST_VIEW_DELEGATE_H_ diff --git a/chrome/browser/ui/views/aura/app_list/browser_command_item.cc b/chrome/browser/ui/views/aura/app_list/browser_command_item.cc new file mode 100644 index 0000000..12c135e --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/browser_command_item.cc @@ -0,0 +1,29 @@ +// 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 "chrome/browser/ui/views/aura/app_list/browser_command_item.h" + +#include "chrome/browser/ui/browser.h" +#include "grit/theme_resources.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/image/image.h" + +BrowserCommandItem::BrowserCommandItem(Browser* browser, + int command_id, + int title_id, + int icon_id) + : browser_(browser), + command_id_(command_id) { + SetTitle(l10n_util::GetStringUTF8(title_id)); + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + SetIcon(*rb.GetImageNamed(icon_id).ToSkBitmap()); +} + +BrowserCommandItem::~BrowserCommandItem() { +} + +void BrowserCommandItem::Activate(int event_flags) { + browser_->ExecuteCommand(command_id_, event_flags); +} diff --git a/chrome/browser/ui/views/aura/app_list/browser_command_item.h b/chrome/browser/ui/views/aura/app_list/browser_command_item.h new file mode 100644 index 0000000..26c896f --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/browser_command_item.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 CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_BROWSER_COMMAND_ITEM_H_ +#define CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_BROWSER_COMMAND_ITEM_H_ +#pragma once + +#include "chrome/browser/ui/views/aura/app_list/chrome_app_list_item.h" + +class Browser; + +class BrowserCommandItem : public ChromeAppListItem { + public: + BrowserCommandItem(Browser* browser, + int command_id, + int title_id, + int icon_id); + virtual ~BrowserCommandItem(); + + private: + // Overridden from ChromeAppListItem: + virtual void Activate(int event_flags) OVERRIDE; + + Browser* browser_; + int command_id_; + + DISALLOW_COPY_AND_ASSIGN(BrowserCommandItem); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_BROWSER_COMMAND_ITEM_H_ diff --git a/chrome/browser/ui/views/aura/app_list/chrome_app_list_item.h b/chrome/browser/ui/views/aura/app_list/chrome_app_list_item.h new file mode 100644 index 0000000..ed676cc --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/chrome_app_list_item.h @@ -0,0 +1,24 @@ +// 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 CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_CHROME_APP_LIST_ITEM_H_ +#define CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_CHROME_APP_LIST_ITEM_H_ +#pragma once + +#include "ui/aura_shell/app_list/app_list_item_model.h" + +// Base class of all chrome app list items. Chrome's AppListViewDelegate assumes +// all items are derived from this class and calls Activate when an item is +// activated. +class ChromeAppListItem : public aura_shell::AppListItemModel { + public: + // Activates the item. |event_flags| holds flags of a mouse/keyboard event + // associated with this activation. + virtual void Activate(int event_flags) = 0; + + protected: + virtual ~ChromeAppListItem() {} +}; + +#endif // CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_CHROME_APP_LIST_ITEM_H_ diff --git a/chrome/browser/ui/views/aura/app_list/extension_app_item.cc b/chrome/browser/ui/views/aura/app_list/extension_app_item.cc new file mode 100644 index 0000000..73a414a --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/extension_app_item.cc @@ -0,0 +1,181 @@ +// 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 "chrome/browser/ui/views/aura/app_list/extension_app_item.h" + +#include "chrome/browser/event_disposition.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_icon_set.h" +#include "chrome/common/extensions/extension_resource.h" +#include "grit/component_extension_resources_map.h" +#include "grit/theme_resources.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/aura_shell/app_list/app_list_item_view.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/codec/png_codec.h" +#include "ui/gfx/image/image.h" + +namespace { + +const ExtensionPrefs* GetExtensionPrefs(Profile* profile) { + return profile->GetExtensionService()->extension_prefs(); +} + +// Gets page ordinal of given |extension| in its app launch page. +StringOrdinal GetExtensionPageOrdinal(Profile* profile, + const Extension* extension) { + const ExtensionPrefs* prefs = GetExtensionPrefs(profile); + StringOrdinal page_ordinal = prefs->GetPageOrdinal(extension->id()); + if (!page_ordinal.IsValid()) { + page_ordinal = extension->id() == extension_misc::kWebStoreAppId ? + prefs->CreateFirstAppPageOrdinal() : prefs->GetNaturalAppPageOrdinal(); + } + + return page_ordinal; +} + +// Gets page index from page ordinal. +int GetExtensionPageIndex(Profile* profile, + const Extension* extension) { + return std::max(0, GetExtensionPrefs(profile)->PageStringOrdinalAsInteger( + GetExtensionPageOrdinal(profile, extension))); +} + +// Gets app launch ordinal of given extension. +StringOrdinal GetExtensionLaunchOrdinal(Profile* profile, + const Extension* extension) { + const ExtensionPrefs* prefs = GetExtensionPrefs(profile); + StringOrdinal app_launch_ordinal = + prefs->GetAppLaunchOrdinal(extension->id()); + if (!app_launch_ordinal.IsValid()) { + StringOrdinal page_ordinal = GetExtensionPageOrdinal(profile, extension); + app_launch_ordinal = extension->id() == extension_misc::kWebStoreAppId ? + prefs->CreateFirstAppLaunchOrdinal(page_ordinal) : + prefs->CreateNextAppLaunchOrdinal(page_ordinal); + } + return app_launch_ordinal; +} + +} + +ExtensionAppItem::ExtensionAppItem(Profile* profile, + const Extension* extension) + : profile_(profile), + extension_id_(extension->id()), + page_index_(GetExtensionPageIndex(profile, extension)), + launch_ordinal_(GetExtensionLaunchOrdinal(profile, extension)) { + SetTitle(extension->name()); + LoadImage(extension); +} + +ExtensionAppItem::~ExtensionAppItem() { +} + +const Extension* ExtensionAppItem::GetExtension() const { + const Extension* extension = + profile_->GetExtensionService()->GetInstalledExtension(extension_id_); + return extension; +} + +void ExtensionAppItem::LoadImage(const Extension* extension) { + ExtensionResource icon = extension->GetIconResource( + aura_shell::AppListItemView::kIconSize, + ExtensionIconSet::MATCH_BIGGER); + if (icon.relative_path().empty()) { + LoadDefaultImage(); + return; + } + + if (extension->location() == Extension::COMPONENT) { + FilePath directory_path = extension->path(); + FilePath relative_path = directory_path.BaseName().Append( + icon.relative_path()); + for (size_t i = 0; i < kComponentExtensionResourcesSize; ++i) { + FilePath bm_resource_path = + FilePath().AppendASCII(kComponentExtensionResources[i].name); +#if defined(OS_WIN) + bm_resource_path = bm_resource_path.NormalizeWindowsPathSeparators(); +#endif + if (relative_path == bm_resource_path) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + int resource = kComponentExtensionResources[i].value; + + base::StringPiece contents = rb.GetRawDataResource(resource); + SkBitmap icon; + if (gfx::PNGCodec::Decode( + reinterpret_cast<const unsigned char*>(contents.data()), + contents.size(), &icon)) { + SetIcon(icon); + return; + } else { + NOTREACHED() << "Unable to decode image resource " << resource; + } + } + } + } + + tracker_.reset(new ImageLoadingTracker(this)); + tracker_->LoadImage(extension, + icon, + gfx::Size(aura_shell::AppListItemView::kIconSize, + aura_shell::AppListItemView::kIconSize), + ImageLoadingTracker::DONT_CACHE); +} + +void ExtensionAppItem::LoadDefaultImage() { + const Extension* extension = GetExtension(); + int resource = IDR_APP_DEFAULT_ICON; + if (extension && extension->id() == extension_misc::kWebStoreAppId) + resource = IDR_WEBSTORE_ICON; + + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + SetIcon(*rb.GetImageNamed(resource).ToSkBitmap()); +} + +void ExtensionAppItem::OnImageLoaded(SkBitmap* image, + const ExtensionResource& resource, + int tracker_index) { + if (image && !image->empty()) + SetIcon(*image); + else + LoadDefaultImage(); +} + +void ExtensionAppItem::Activate(int event_flags) { + const Extension* extension = GetExtension(); + if (!extension) + return; + + WindowOpenDisposition disposition = + browser::DispositionFromEventFlags(event_flags); + + GURL url; + if (extension_id_ == extension_misc::kWebStoreAppId) + url = extension->GetFullLaunchURL(); + + if (disposition == NEW_FOREGROUND_TAB || disposition == NEW_BACKGROUND_TAB) { + // Opens in a tab. + Browser::OpenApplication( + profile_, extension, extension_misc::LAUNCH_TAB, url, disposition); + } else if (disposition == NEW_WINDOW) { + // Force a new window open. + Browser::OpenApplication( + profile_, extension, extension_misc::LAUNCH_WINDOW, url, + disposition); + } else { + // Look at preference to find the right launch container. If no preference + // is set, launch as a regular tab. + extension_misc::LaunchContainer launch_container = + profile_->GetExtensionService()->extension_prefs()->GetLaunchContainer( + extension, ExtensionPrefs::LAUNCH_REGULAR); + + Browser::OpenApplication( + profile_, extension, launch_container, GURL(url), + NEW_FOREGROUND_TAB); + } +} diff --git a/chrome/browser/ui/views/aura/app_list/extension_app_item.h b/chrome/browser/ui/views/aura/app_list/extension_app_item.h new file mode 100644 index 0000000..7fbc463 --- /dev/null +++ b/chrome/browser/ui/views/aura/app_list/extension_app_item.h @@ -0,0 +1,64 @@ +// 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 CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_EXTENSION_APP_ITEM_H_ +#define CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_EXTENSION_APP_ITEM_H_ +#pragma once + +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/image_loading_tracker.h" +#include "chrome/browser/ui/views/aura/app_list/chrome_app_list_item.h" +#include "chrome/common/string_ordinal.h" + +class Extension; +class ExtensionResource; +class Profile; +class SkBitmap; + +// ExtensionAppItem represents an extension app in app list. +class ExtensionAppItem : public ChromeAppListItem, + public ImageLoadingTracker::Observer { + public: + ExtensionAppItem(Profile* profile, const Extension* extension); + virtual ~ExtensionAppItem(); + + // Gets extension associated with this model. Returns NULL if extension + // no longer exists. + const Extension* GetExtension() const; + + int page_index() const { + return page_index_; + } + + const StringOrdinal& launch_ordinal() const { + return launch_ordinal_; + } + + private: + // Loads extension icon. + void LoadImage(const Extension* extension); + + // Loads default extension icon. + void LoadDefaultImage(); + + // Overridden from ImageLoadingTracker::Observer + virtual void OnImageLoaded(SkBitmap* image, + const ExtensionResource& resource, + int tracker_index) OVERRIDE; + + // Overridden from ChromeAppListItem: + virtual void Activate(int event_flags) OVERRIDE; + + Profile* profile_; + const std::string extension_id_; + + const int page_index_; + const StringOrdinal launch_ordinal_; + + scoped_ptr<ImageLoadingTracker> tracker_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionAppItem); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_AURA_APP_LIST_EXTENSION_APP_ITEM_H_ diff --git a/chrome/browser/ui/views/aura/chrome_shell_delegate.cc b/chrome/browser/ui/views/aura/chrome_shell_delegate.cc index 7750f8c..6774bb9 100644 --- a/chrome/browser/ui/views/aura/chrome_shell_delegate.cc +++ b/chrome/browser/ui/views/aura/chrome_shell_delegate.cc @@ -7,6 +7,8 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/views/aura/app_list_window.h" +#include "chrome/browser/ui/views/aura/app_list/app_list_model_builder.h" +#include "chrome/browser/ui/views/aura/app_list/app_list_view_delegate.h" #include "chrome/browser/ui/views/aura/status_area_host_aura.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "ui/aura/window.h" @@ -64,6 +66,18 @@ void ChromeShellDelegate::RequestAppListWidget( new AppListWindow(bounds, callback); } +void ChromeShellDelegate::BuildAppListModel(aura_shell::AppListModel* model) { + AppListModelBuilder builder(ProfileManager::GetDefaultProfile(), + model); + builder.Build(); +} + +aura_shell::AppListViewDelegate* +ChromeShellDelegate::CreateAppListViewDelegate() { + // Shell will own the created delegate. + return new AppListViewDelegate; +} + void ChromeShellDelegate::LauncherItemClicked( const aura_shell::LauncherItem& item) { aura_shell::ActivateWindow(item.window); diff --git a/chrome/browser/ui/views/aura/chrome_shell_delegate.h b/chrome/browser/ui/views/aura/chrome_shell_delegate.h index a524f30..99046e0 100644 --- a/chrome/browser/ui/views/aura/chrome_shell_delegate.h +++ b/chrome/browser/ui/views/aura/chrome_shell_delegate.h @@ -41,6 +41,8 @@ class ChromeShellDelegate : public aura_shell::ShellDelegate { virtual void RequestAppListWidget( const gfx::Rect& bounds, const SetWidgetCallback& callback) OVERRIDE; + virtual void BuildAppListModel(aura_shell::AppListModel* model) OVERRIDE; + virtual aura_shell::AppListViewDelegate* CreateAppListViewDelegate() OVERRIDE; virtual void LauncherItemClicked( const aura_shell::LauncherItem& item) OVERRIDE; virtual bool ConfigureLauncherItem(aura_shell::LauncherItem* item) OVERRIDE; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index edef9ff..ba43d24 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -472,7 +472,6 @@ 'browser/chromeos/dbus/update_engine_client.h', 'browser/chromeos/disks/disk_mount_manager.cc', 'browser/chromeos/disks/disk_mount_manager.h', - 'browser/chromeos/drop_shadow_label.cc', 'browser/chromeos/enterprise_extension_observer.cc', 'browser/chromeos/enterprise_extension_observer.h', 'browser/chromeos/extensions/file_browser_event_router.cc', @@ -3352,6 +3351,15 @@ 'browser/ui/views/appcache_info_view.h', 'browser/ui/views/aura/app_list_window.cc', 'browser/ui/views/aura/app_list_window.h', + 'browser/ui/views/aura/app_list/app_list_model_builder.cc', + 'browser/ui/views/aura/app_list/app_list_model_builder.h', + 'browser/ui/views/aura/app_list/app_list_view_delegate.cc', + 'browser/ui/views/aura/app_list/app_list_view_delegate.h', + 'browser/ui/views/aura/app_list/browser_command_item.cc', + 'browser/ui/views/aura/app_list/browser_command_item.h', + 'browser/ui/views/aura/app_list/chrome_app_list_item.h', + 'browser/ui/views/aura/app_list/extension_app_item.cc', + 'browser/ui/views/aura/app_list/extension_app_item.h', 'browser/ui/views/aura/chrome_shell_delegate.cc', 'browser/ui/views/aura/chrome_shell_delegate.h', 'browser/ui/views/aura/launcher_icon_updater.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 90bced0..fe296cc 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1917,6 +1917,7 @@ 'browser/ui/toolbar/encoding_menu_controller_unittest.cc', 'browser/ui/toolbar/toolbar_model_unittest.cc', 'browser/ui/toolbar/wrench_menu_model_unittest.cc', + 'browser/ui/views/aura/app_list/app_list_model_builder_unittest.cc', 'browser/ui/views/accessibility_event_router_views_unittest.cc', 'browser/ui/views/bookmarks/bookmark_context_menu_test.cc', 'browser/ui/views/bookmarks/bookmark_editor_view_unittest.cc', diff --git a/chrome/test/data/extensions/app_list/Extensions/dceacbkfkmllgmjmbhgkpjegnodmildf/1.0/manifest.json b/chrome/test/data/extensions/app_list/Extensions/dceacbkfkmllgmjmbhgkpjegnodmildf/1.0/manifest.json new file mode 100644 index 0000000..8b2183c --- /dev/null +++ b/chrome/test/data/extensions/app_list/Extensions/dceacbkfkmllgmjmbhgkpjegnodmildf/1.0/manifest.json @@ -0,0 +1,12 @@ +{ + "key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDbh1pj7Q+vZzotbfPfe1Q4AIeJc3gU1xhTSMtjB0UZH+G3ckScJYTnFCTkFAn/mEOtn7e+SZ9rhDf4k6X7Qf6BzK3PLNn3+2Hb/F0NC57hiWI2UyhXY2dl2ry6VENkuyo1QpEMGH5FB5tC2rcuivG8ipBbUWLoQLMbegUdOLXoNK4tGvKwlGa1B0QPAMIkEw3ZlerckC8e+tWC38WvHxy1EM5VeK8k4GcrDEltoVByprTe/8VTzvEsFFYljpIzbSTi6mKOhY2sdl0EfCpXT4dSCeSe81O8liMU1yYKBQCbzguASV7yzZMX08tb96MOpx0NYbYXt03Zyj3xlbep5n4l", + "version": "1.0", + "name": "Hosted App", + "manifest_version": 2, + "app": { + "urls": [ "*://www.google.com/" ], + "launch": { + "web_url": "http://www.google.com" + } + } +} diff --git a/chrome/test/data/extensions/app_list/Extensions/ddljohincgifmhhihcegmecmeohlfngm/1.0/manifest.json b/chrome/test/data/extensions/app_list/Extensions/ddljohincgifmhhihcegmecmeohlfngm/1.0/manifest.json new file mode 100644 index 0000000..f777866 --- /dev/null +++ b/chrome/test/data/extensions/app_list/Extensions/ddljohincgifmhhihcegmecmeohlfngm/1.0/manifest.json @@ -0,0 +1,6 @@ +{ + "key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDUfs/dEfHkfc10Ud1qbzeBNPetmVRDyqDtMGE8tP7QrPBvqsmPHkT3F9QF6HGMd9doaWezO5KPCQIw0Sxck4/6U3VNme77rXvqexGYoYSuy+Q51J+TikJDngWIgUuY5AVg98PbAsyK8qzbVrQ/rPygP3NnZz1GTV6mHbihpRfz5t969i1f6tPtjs8rQYFfauknVaIDeSR3icUfeqrtoxnJWsaY5Tk0hrJJdvG/TF8pKseps+aVEpsJETzHUxFOa6ES9tNfLsZfBAZtmD58twKYsBV6eg/TXJveumNG0dfmt7KviwK8dOm333V6CT/++KGQ6uW5YHwrjhOYu+chLt5f", + "version": "1.0", + "name": "Dummy Extension 1", + "manifest_version": 2 +} diff --git a/chrome/test/data/extensions/app_list/Extensions/emfkafnhnpcmabnnkckkchdilgeoekbo/1.0/main.html b/chrome/test/data/extensions/app_list/Extensions/emfkafnhnpcmabnnkckkchdilgeoekbo/1.0/main.html new file mode 100644 index 0000000..18ecdcb --- /dev/null +++ b/chrome/test/data/extensions/app_list/Extensions/emfkafnhnpcmabnnkckkchdilgeoekbo/1.0/main.html @@ -0,0 +1 @@ +<html></html> diff --git a/chrome/test/data/extensions/app_list/Extensions/emfkafnhnpcmabnnkckkchdilgeoekbo/1.0/manifest.json b/chrome/test/data/extensions/app_list/Extensions/emfkafnhnpcmabnnkckkchdilgeoekbo/1.0/manifest.json new file mode 100644 index 0000000..bae4986 --- /dev/null +++ b/chrome/test/data/extensions/app_list/Extensions/emfkafnhnpcmabnnkckkchdilgeoekbo/1.0/manifest.json @@ -0,0 +1,11 @@ +{ + "key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQCxUatX2sx1yjeTAnu8ydtVhdD/JPn2wrmK7rX1Qko7FY4BRA733YPuY7JTWdKDbNU4OKaJOjexkz3klsj0vvTwRJgNghSgYePTPHZfWtRzy7WVG3KOQX2IgNMuOMWE/r5Eyg84Zi+0vLIcTzaqdAOcFwGYhp0/jRvDWDRpoQLKld6p58c/kORJ4sZrZMnp22OSz0rrLZseDEH+4ZJt0GWPUwzlKKLAsC8WthW5ntNjzkjxJSpRrM+WF7nr/dmYUX3791rRia7rtJFNx66AZroZ3PT7H1uOkr7f5vQyZbQrtMfmoxUtuu0yGGJS3JPw9i+0Pg7iHUQkRYUG/dNVYh//", + "version": "1.0", + "name": "Packaged App 1", + "manifest_version": 2, + "app": { + "launch": { + "local_path": "main.html" + } + } +} diff --git a/chrome/test/data/extensions/app_list/Extensions/jlklkagmeajbjiobondfhiekepofmljl/1.0/main.html b/chrome/test/data/extensions/app_list/Extensions/jlklkagmeajbjiobondfhiekepofmljl/1.0/main.html new file mode 100644 index 0000000..18ecdcb --- /dev/null +++ b/chrome/test/data/extensions/app_list/Extensions/jlklkagmeajbjiobondfhiekepofmljl/1.0/main.html @@ -0,0 +1 @@ +<html></html> diff --git a/chrome/test/data/extensions/app_list/Extensions/jlklkagmeajbjiobondfhiekepofmljl/1.0/manifest.json b/chrome/test/data/extensions/app_list/Extensions/jlklkagmeajbjiobondfhiekepofmljl/1.0/manifest.json new file mode 100644 index 0000000..40293cf --- /dev/null +++ b/chrome/test/data/extensions/app_list/Extensions/jlklkagmeajbjiobondfhiekepofmljl/1.0/manifest.json @@ -0,0 +1,11 @@ +{ + "key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDhZCj7XcmF9ADcbuJqDFsBaILQEMMkrdgAQVHh0LFKKNTVOtNRJWhodme3abYKDa4DqXup05Ao6bTntvj0XekcyY35xPTTH6//4FNfkv9Nc7UXBO2iN+c9fodgiR8j0c1sePmbxG2zog7tGfujBldqE+E6zix8arTvDme144WpLRVasszq6qIHRfsg0X8GSlRHG6cm+HVJ8ZcO4FrqX4eTmr0UrQf1JPUfZccKpmtjzKVR9HhOXE9XuSpWZuw4QatE/ENtjVBcb8JUhbUJw3g8Qi6xGjCIISsj/RSEUidVFUt65VnQ5d/DrXBjFk5k26bQscLKXHn2mRUgQau8OZDv", + "version": "1.0", + "name": "Packaged App 2", + "manifest_version": 2, + "app": { + "launch": { + "local_path": "main.html" + } + } +} diff --git a/chrome/test/data/extensions/app_list/Preferences b/chrome/test/data/extensions/app_list/Preferences new file mode 100644 index 0000000..bb1de10 --- /dev/null +++ b/chrome/test/data/extensions/app_list/Preferences @@ -0,0 +1,72 @@ +{ + "extensions": { + "settings": { + "dceacbkfkmllgmjmbhgkpjegnodmildf": { + "location": 1, + "path": "dceacbkfkmllgmjmbhgkpjegnodmildf/1.0", + "app_launcher_ordinal": "n", + "page_ordinal": "t", + "state": 1, + "install_time": "12968795380420751", + "manifest": { + "key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDbh1pj7Q+vZzotbfPfe1Q4AIeJc3gU1xhTSMtjB0UZH+G3ckScJYTnFCTkFAn/mEOtn7e+SZ9rhDf4k6X7Qf6BzK3PLNn3+2Hb/F0NC57hiWI2UyhXY2dl2ry6VENkuyo1QpEMGH5FB5tC2rcuivG8ipBbUWLoQLMbegUdOLXoNK4tGvKwlGa1B0QPAMIkEw3ZlerckC8e+tWC38WvHxy1EM5VeK8k4GcrDEltoVByprTe/8VTzvEsFFYljpIzbSTi6mKOhY2sdl0EfCpXT4dSCeSe81O8liMU1yYKBQCbzguASV7yzZMX08tb96MOpx0NYbYXt03Zyj3xlbep5n4l", + "version": "1.0", + "name": "Hosted App", + "app": { + "urls": [ "*://www.google.com/" ], + "launch": { + "web_url": "http://www.google.com" + } + } + } + }, + "ddljohincgifmhhihcegmecmeohlfngm": { + "location": 1, + "path": "ddljohincgifmhhihcegmecmeohlfngm/1.0", + "state": 1, + "install_time": "12968795380420751", + "manifest": { + "key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDUfs/dEfHkfc10Ud1qbzeBNPetmVRDyqDtMGE8tP7QrPBvqsmPHkT3F9QF6HGMd9doaWezO5KPCQIw0Sxck4/6U3VNme77rXvqexGYoYSuy+Q51J+TikJDngWIgUuY5AVg98PbAsyK8qzbVrQ/rPygP3NnZz1GTV6mHbihpRfz5t969i1f6tPtjs8rQYFfauknVaIDeSR3icUfeqrtoxnJWsaY5Tk0hrJJdvG/TF8pKseps+aVEpsJETzHUxFOa6ES9tNfLsZfBAZtmD58twKYsBV6eg/TXJveumNG0dfmt7KviwK8dOm333V6CT/++KGQ6uW5YHwrjhOYu+chLt5f", + "version": "1.0", + "name": "Dummy Extension 1" + } + }, + "emfkafnhnpcmabnnkckkchdilgeoekbo": { + "location": 1, + "path": "emfkafnhnpcmabnnkckkchdilgeoekbo/1.0", + "app_launcher_ordinal": "n", + "page_ordinal": "n", + "state": 1, + "install_time": "12968795380420751", + "manifest": { + "key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQCxUatX2sx1yjeTAnu8ydtVhdD/JPn2wrmK7rX1Qko7FY4BRA733YPuY7JTWdKDbNU4OKaJOjexkz3klsj0vvTwRJgNghSgYePTPHZfWtRzy7WVG3KOQX2IgNMuOMWE/r5Eyg84Zi+0vLIcTzaqdAOcFwGYhp0/jRvDWDRpoQLKld6p58c/kORJ4sZrZMnp22OSz0rrLZseDEH+4ZJt0GWPUwzlKKLAsC8WthW5ntNjzkjxJSpRrM+WF7nr/dmYUX3791rRia7rtJFNx66AZroZ3PT7H1uOkr7f5vQyZbQrtMfmoxUtuu0yGGJS3JPw9i+0Pg7iHUQkRYUG/dNVYh//", + "version": "1.0", + "name": "Packaged App 1", + "app": { + "launch": { + "local_path": "main.html" + } + } + } + }, + "jlklkagmeajbjiobondfhiekepofmljl": { + "location": 1, + "path": "jlklkagmeajbjiobondfhiekepofmljl/1.0", + "app_launcher_ordinal": "t", + "page_ordinal": "n", + "state": 1, + "install_time": "12968795380420751", + "manifest": { + "key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDhZCj7XcmF9ADcbuJqDFsBaILQEMMkrdgAQVHh0LFKKNTVOtNRJWhodme3abYKDa4DqXup05Ao6bTntvj0XekcyY35xPTTH6//4FNfkv9Nc7UXBO2iN+c9fodgiR8j0c1sePmbxG2zog7tGfujBldqE+E6zix8arTvDme144WpLRVasszq6qIHRfsg0X8GSlRHG6cm+HVJ8ZcO4FrqX4eTmr0UrQf1JPUfZccKpmtjzKVR9HhOXE9XuSpWZuw4QatE/ENtjVBcb8JUhbUJw3g8Qi6xGjCIISsj/RSEUidVFUt65VnQ5d/DrXBjFk5k26bQscLKXHn2mRUgQau8OZDv", + "version": "1.0", + "name": "Packaged App 2", + "app": { + "launch": { + "local_path": "main.html" + } + } + } + } + } + } +} 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/chrome/browser/chromeos/drop_shadow_label.h b/ui/aura_shell/app_list/drop_shadow_label.h index e64f12d..ded5e31 100644 --- a/chrome/browser/chromeos/drop_shadow_label.h +++ b/ui/aura_shell/app_list/drop_shadow_label.h @@ -2,14 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_CHROMEOS_DROP_SHADOW_LABEL_H_ -#define CHROME_BROWSER_CHROMEOS_DROP_SHADOW_LABEL_H_ +#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 chromeos { +namespace aura_shell { ///////////////////////////////////////////////////////////////////////////// // @@ -49,6 +49,6 @@ class DropShadowLabel : public views::Label { DISALLOW_COPY_AND_ASSIGN(DropShadowLabel); }; -} // namespace chromeos +} // namespace aura_shell -#endif // CHROME_BROWSER_CHROMEOS_DROP_SHADOW_LABEL_H_ +#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 ddfc949..1e1d9a1 100644 --- a/ui/aura_shell/aura_shell.gyp +++ b/ui/aura_shell/aura_shell.gyp @@ -33,10 +33,29 @@ ], 'sources': [ # All .cc, .h under views, except unittests - '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': [ + '../../ash/shell/app_list.cc', '../../ash/shell/bubble.cc', '../../ash/shell/example_factory.h', '../../ash/shell/lock_view.cc', 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/shell.cc b/ui/aura_shell/shell.cc index bfa233e..f73c39e 100644 --- a/ui/aura_shell/shell.cc +++ b/ui/aura_shell/shell.cc @@ -15,7 +15,7 @@ #include "ui/aura/root_window.h" #include "ui/aura/layout_manager.h" #include "ui/aura/window.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/base/ui_base_exports.cc b/ui/base/ui_base_exports.cc index 5759e9b..5d7f51d 100644 --- a/ui/base/ui_base_exports.cc +++ b/ui/base/ui_base_exports.cc @@ -8,4 +8,5 @@ // resulting dynamic library (ui.dll). #include "ui/base/accelerators/accelerator.h" +#include "ui/base/models/list_model_observer.h" #include "ui/base/models/table_model_observer.h" @@ -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', |