blob: afcb823bb3d19256ec44dccf48284f4c152311e0 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/views/controls/menu/menu_model_adapter.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/menu_model.h"
#include "ui/base/models/menu_model_delegate.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/test/views_test_base.h"
namespace {
// Base command id for test menu and its submenu.
const int kRootIdBase = 100;
const int kSubmenuIdBase = 200;
class MenuModelBase : public ui::MenuModel {
public:
explicit MenuModelBase(int command_id_base)
: command_id_base_(command_id_base),
last_activation_(-1) {
}
virtual ~MenuModelBase() {
}
// ui::MenuModel implementation:
virtual bool HasIcons() const OVERRIDE {
return false;
}
virtual int GetItemCount() const OVERRIDE {
return static_cast<int>(items_.size());
}
virtual ItemType GetTypeAt(int index) const OVERRIDE {
return items_[index].type;
}
virtual ui::MenuSeparatorType GetSeparatorTypeAt(
int index) const OVERRIDE {
return ui::NORMAL_SEPARATOR;
}
virtual int GetCommandIdAt(int index) const OVERRIDE {
return index + command_id_base_;
}
virtual base::string16 GetLabelAt(int index) const OVERRIDE {
return items_[index].label;
}
virtual bool IsItemDynamicAt(int index) const OVERRIDE {
return false;
}
virtual const gfx::FontList* GetLabelFontListAt(int index) const OVERRIDE {
return NULL;
}
virtual bool GetAcceleratorAt(int index,
ui::Accelerator* accelerator) const OVERRIDE {
return false;
}
virtual bool IsItemCheckedAt(int index) const OVERRIDE {
return false;
}
virtual int GetGroupIdAt(int index) const OVERRIDE {
return 0;
}
virtual bool GetIconAt(int index, gfx::Image* icon) OVERRIDE {
return false;
}
virtual ui::ButtonMenuItemModel* GetButtonMenuItemAt(
int index) const OVERRIDE {
return NULL;
}
virtual bool IsEnabledAt(int index) const OVERRIDE {
return true;
}
virtual bool IsVisibleAt(int index) const OVERRIDE {
return true;
}
virtual MenuModel* GetSubmenuModelAt(int index) const OVERRIDE {
return items_[index].submenu;
}
virtual void HighlightChangedTo(int index) OVERRIDE {
}
virtual void ActivatedAt(int index) OVERRIDE {
set_last_activation(index);
}
virtual void ActivatedAt(int index, int event_flags) OVERRIDE {
ActivatedAt(index);
}
virtual void MenuWillShow() OVERRIDE {
}
virtual void MenuClosed() OVERRIDE {
}
virtual void SetMenuModelDelegate(
ui::MenuModelDelegate* delegate) OVERRIDE {
}
virtual ui::MenuModelDelegate* GetMenuModelDelegate() const OVERRIDE {
return NULL;
}
// Item definition.
struct Item {
Item(ItemType item_type,
const std::string& item_label,
ui::MenuModel* item_submenu)
: type(item_type),
label(base::ASCIIToUTF16(item_label)),
submenu(item_submenu) {
}
ItemType type;
base::string16 label;
ui::MenuModel* submenu;
};
const Item& GetItemDefinition(int index) {
return items_[index];
}
// Access index argument to ActivatedAt().
int last_activation() const { return last_activation_; }
void set_last_activation(int last_activation) {
last_activation_ = last_activation;
}
protected:
std::vector<Item> items_;
private:
int command_id_base_;
int last_activation_;
DISALLOW_COPY_AND_ASSIGN(MenuModelBase);
};
class SubmenuModel : public MenuModelBase {
public:
SubmenuModel() : MenuModelBase(kSubmenuIdBase) {
items_.push_back(Item(TYPE_COMMAND, "submenu item 0", NULL));
items_.push_back(Item(TYPE_COMMAND, "submenu item 1", NULL));
}
virtual ~SubmenuModel() {
}
private:
DISALLOW_COPY_AND_ASSIGN(SubmenuModel);
};
class RootModel : public MenuModelBase {
public:
RootModel() : MenuModelBase(kRootIdBase) {
submenu_model_.reset(new SubmenuModel);
items_.push_back(Item(TYPE_COMMAND, "command 0", NULL));
items_.push_back(Item(TYPE_CHECK, "check 1", NULL));
items_.push_back(Item(TYPE_SEPARATOR, "", NULL));
items_.push_back(Item(TYPE_SUBMENU, "submenu 3", submenu_model_.get()));
items_.push_back(Item(TYPE_RADIO, "radio 4", NULL));
}
virtual ~RootModel() {
}
private:
scoped_ptr<MenuModel> submenu_model_;
DISALLOW_COPY_AND_ASSIGN(RootModel);
};
} // namespace
namespace views {
typedef ViewsTestBase MenuModelAdapterTest;
TEST_F(MenuModelAdapterTest, BasicTest) {
// Build model and adapter.
RootModel model;
views::MenuModelAdapter delegate(&model);
// Create menu. Build menu twice to check that rebuilding works properly.
MenuItemView* menu = new views::MenuItemView(&delegate);
// MenuRunner takes ownership of menu.
scoped_ptr<MenuRunner> menu_runner(new MenuRunner(menu, 0));
delegate.BuildMenu(menu);
delegate.BuildMenu(menu);
EXPECT_TRUE(menu->HasSubmenu());
// Check top level menu items.
views::SubmenuView* item_container = menu->GetSubmenu();
EXPECT_EQ(5, item_container->child_count());
for (int i = 0; i < item_container->child_count(); ++i) {
const MenuModelBase::Item& model_item = model.GetItemDefinition(i);
const int id = i + kRootIdBase;
MenuItemView* item = menu->GetMenuItemByID(id);
if (!item) {
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model_item.type);
continue;
}
// Check placement.
EXPECT_EQ(i, menu->GetSubmenu()->GetIndexOf(item));
// Check type.
switch (model_item.type) {
case ui::MenuModel::TYPE_COMMAND:
EXPECT_EQ(views::MenuItemView::NORMAL, item->GetType());
break;
case ui::MenuModel::TYPE_CHECK:
EXPECT_EQ(views::MenuItemView::CHECKBOX, item->GetType());
break;
case ui::MenuModel::TYPE_RADIO:
EXPECT_EQ(views::MenuItemView::RADIO, item->GetType());
break;
case ui::MenuModel::TYPE_SEPARATOR:
case ui::MenuModel::TYPE_BUTTON_ITEM:
break;
case ui::MenuModel::TYPE_SUBMENU:
EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
break;
}
// Check activation.
static_cast<views::MenuDelegate*>(&delegate)->ExecuteCommand(id);
EXPECT_EQ(i, model.last_activation());
model.set_last_activation(-1);
}
// Check submenu items.
views::MenuItemView* submenu = menu->GetMenuItemByID(103);
views::SubmenuView* subitem_container = submenu->GetSubmenu();
EXPECT_EQ(2, subitem_container->child_count());
for (int i = 0; i < subitem_container->child_count(); ++i) {
MenuModelBase* submodel = static_cast<MenuModelBase*>(
model.GetSubmenuModelAt(3));
EXPECT_TRUE(submodel);
const MenuModelBase::Item& model_item = submodel->GetItemDefinition(i);
const int id = i + kSubmenuIdBase;
MenuItemView* item = menu->GetMenuItemByID(id);
if (!item) {
EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model_item.type);
continue;
}
// Check placement.
EXPECT_EQ(i, submenu->GetSubmenu()->GetIndexOf(item));
// Check type.
switch (model_item.type) {
case ui::MenuModel::TYPE_COMMAND:
EXPECT_EQ(views::MenuItemView::NORMAL, item->GetType());
break;
case ui::MenuModel::TYPE_CHECK:
EXPECT_EQ(views::MenuItemView::CHECKBOX, item->GetType());
break;
case ui::MenuModel::TYPE_RADIO:
EXPECT_EQ(views::MenuItemView::RADIO, item->GetType());
break;
case ui::MenuModel::TYPE_SEPARATOR:
case ui::MenuModel::TYPE_BUTTON_ITEM:
break;
case ui::MenuModel::TYPE_SUBMENU:
EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
break;
}
// Check activation.
static_cast<views::MenuDelegate*>(&delegate)->ExecuteCommand(id);
EXPECT_EQ(i, submodel->last_activation());
submodel->set_last_activation(-1);
}
// Check that selecting the root item is safe. The MenuModel does
// not care about the root so MenuModelAdapter should do nothing
// (not hit the NOTREACHED check) when the root is selected.
static_cast<views::MenuDelegate*>(&delegate)->SelectionChanged(menu);
}
} // namespace views