1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/views/controls/menu/menu_model_adapter.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "ui/base/models/menu_model.h"
9 #include "ui/base/models/menu_model_delegate.h"
10 #include "ui/views/controls/menu/menu_item_view.h"
11 #include "ui/views/controls/menu/menu_runner.h"
12 #include "ui/views/controls/menu/submenu_view.h"
13 #include "ui/views/test/views_test_base.h"
17 // Base command id for test menu and its submenu.
18 const int kRootIdBase
= 100;
19 const int kSubmenuIdBase
= 200;
21 class MenuModelBase
: public ui::MenuModel
{
23 explicit MenuModelBase(int command_id_base
)
24 : command_id_base_(command_id_base
),
25 last_activation_(-1) {
28 ~MenuModelBase() override
{}
30 // ui::MenuModel implementation:
32 bool HasIcons() const override
{ return false; }
34 int GetItemCount() const override
{ return static_cast<int>(items_
.size()); }
36 ItemType
GetTypeAt(int index
) const override
{ return items_
[index
].type
; }
38 ui::MenuSeparatorType
GetSeparatorTypeAt(int index
) const override
{
39 return ui::NORMAL_SEPARATOR
;
42 int GetCommandIdAt(int index
) const override
{
43 return index
+ command_id_base_
;
46 base::string16
GetLabelAt(int index
) const override
{
47 return items_
[index
].label
;
50 bool IsItemDynamicAt(int index
) const override
{ return false; }
52 const gfx::FontList
* GetLabelFontListAt(int index
) const override
{
56 bool GetAcceleratorAt(int index
,
57 ui::Accelerator
* accelerator
) const override
{
61 bool IsItemCheckedAt(int index
) const override
{ return false; }
63 int GetGroupIdAt(int index
) const override
{ return 0; }
65 bool GetIconAt(int index
, gfx::Image
* icon
) override
{ return false; }
67 ui::ButtonMenuItemModel
* GetButtonMenuItemAt(int index
) const override
{
71 bool IsEnabledAt(int index
) const override
{ return true; }
73 bool IsVisibleAt(int index
) const override
{ return true; }
75 MenuModel
* GetSubmenuModelAt(int index
) const override
{
76 return items_
[index
].submenu
;
79 void HighlightChangedTo(int index
) override
{}
81 void ActivatedAt(int index
) override
{ set_last_activation(index
); }
83 void ActivatedAt(int index
, int event_flags
) override
{ ActivatedAt(index
); }
85 void MenuWillShow() override
{}
87 void MenuClosed() override
{}
89 void SetMenuModelDelegate(ui::MenuModelDelegate
* delegate
) override
{}
91 ui::MenuModelDelegate
* GetMenuModelDelegate() const override
{ return NULL
; }
95 Item(ItemType item_type
,
96 const std::string
& item_label
,
97 ui::MenuModel
* item_submenu
)
99 label(base::ASCIIToUTF16(item_label
)),
100 submenu(item_submenu
) {
104 base::string16 label
;
105 ui::MenuModel
* submenu
;
108 const Item
& GetItemDefinition(int index
) {
109 return items_
[index
];
112 // Access index argument to ActivatedAt().
113 int last_activation() const { return last_activation_
; }
114 void set_last_activation(int last_activation
) {
115 last_activation_
= last_activation
;
119 std::vector
<Item
> items_
;
122 int command_id_base_
;
123 int last_activation_
;
125 DISALLOW_COPY_AND_ASSIGN(MenuModelBase
);
128 class SubmenuModel
: public MenuModelBase
{
130 SubmenuModel() : MenuModelBase(kSubmenuIdBase
) {
131 items_
.push_back(Item(TYPE_COMMAND
, "submenu item 0", NULL
));
132 items_
.push_back(Item(TYPE_COMMAND
, "submenu item 1", NULL
));
135 ~SubmenuModel() override
{}
138 DISALLOW_COPY_AND_ASSIGN(SubmenuModel
);
141 class RootModel
: public MenuModelBase
{
143 RootModel() : MenuModelBase(kRootIdBase
) {
144 submenu_model_
.reset(new SubmenuModel
);
146 items_
.push_back(Item(TYPE_COMMAND
, "command 0", NULL
));
147 items_
.push_back(Item(TYPE_CHECK
, "check 1", NULL
));
148 items_
.push_back(Item(TYPE_SEPARATOR
, "", NULL
));
149 items_
.push_back(Item(TYPE_SUBMENU
, "submenu 3", submenu_model_
.get()));
150 items_
.push_back(Item(TYPE_RADIO
, "radio 4", NULL
));
153 ~RootModel() override
{}
156 scoped_ptr
<MenuModel
> submenu_model_
;
158 DISALLOW_COPY_AND_ASSIGN(RootModel
);
165 typedef ViewsTestBase MenuModelAdapterTest
;
167 TEST_F(MenuModelAdapterTest
, BasicTest
) {
168 // Build model and adapter.
170 views::MenuModelAdapter
delegate(&model
);
172 // Create menu. Build menu twice to check that rebuilding works properly.
173 MenuItemView
* menu
= new views::MenuItemView(&delegate
);
174 // MenuRunner takes ownership of menu.
175 scoped_ptr
<MenuRunner
> menu_runner(new MenuRunner(menu
, 0));
176 delegate
.BuildMenu(menu
);
177 delegate
.BuildMenu(menu
);
178 EXPECT_TRUE(menu
->HasSubmenu());
180 // Check top level menu items.
181 views::SubmenuView
* item_container
= menu
->GetSubmenu();
182 EXPECT_EQ(5, item_container
->child_count());
184 for (int i
= 0; i
< item_container
->child_count(); ++i
) {
185 const MenuModelBase::Item
& model_item
= model
.GetItemDefinition(i
);
187 const int id
= i
+ kRootIdBase
;
188 MenuItemView
* item
= menu
->GetMenuItemByID(id
);
190 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR
, model_item
.type
);
195 EXPECT_EQ(i
, menu
->GetSubmenu()->GetIndexOf(item
));
198 switch (model_item
.type
) {
199 case ui::MenuModel::TYPE_COMMAND
:
200 EXPECT_EQ(views::MenuItemView::NORMAL
, item
->GetType());
202 case ui::MenuModel::TYPE_CHECK
:
203 EXPECT_EQ(views::MenuItemView::CHECKBOX
, item
->GetType());
205 case ui::MenuModel::TYPE_RADIO
:
206 EXPECT_EQ(views::MenuItemView::RADIO
, item
->GetType());
208 case ui::MenuModel::TYPE_SEPARATOR
:
209 case ui::MenuModel::TYPE_BUTTON_ITEM
:
211 case ui::MenuModel::TYPE_SUBMENU
:
212 EXPECT_EQ(views::MenuItemView::SUBMENU
, item
->GetType());
217 static_cast<views::MenuDelegate
*>(&delegate
)->ExecuteCommand(id
);
218 EXPECT_EQ(i
, model
.last_activation());
219 model
.set_last_activation(-1);
222 // Check submenu items.
223 views::MenuItemView
* submenu
= menu
->GetMenuItemByID(103);
224 views::SubmenuView
* subitem_container
= submenu
->GetSubmenu();
225 EXPECT_EQ(2, subitem_container
->child_count());
227 for (int i
= 0; i
< subitem_container
->child_count(); ++i
) {
228 MenuModelBase
* submodel
= static_cast<MenuModelBase
*>(
229 model
.GetSubmenuModelAt(3));
230 EXPECT_TRUE(submodel
);
232 const MenuModelBase::Item
& model_item
= submodel
->GetItemDefinition(i
);
234 const int id
= i
+ kSubmenuIdBase
;
235 MenuItemView
* item
= menu
->GetMenuItemByID(id
);
237 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR
, model_item
.type
);
242 EXPECT_EQ(i
, submenu
->GetSubmenu()->GetIndexOf(item
));
245 switch (model_item
.type
) {
246 case ui::MenuModel::TYPE_COMMAND
:
247 EXPECT_EQ(views::MenuItemView::NORMAL
, item
->GetType());
249 case ui::MenuModel::TYPE_CHECK
:
250 EXPECT_EQ(views::MenuItemView::CHECKBOX
, item
->GetType());
252 case ui::MenuModel::TYPE_RADIO
:
253 EXPECT_EQ(views::MenuItemView::RADIO
, item
->GetType());
255 case ui::MenuModel::TYPE_SEPARATOR
:
256 case ui::MenuModel::TYPE_BUTTON_ITEM
:
258 case ui::MenuModel::TYPE_SUBMENU
:
259 EXPECT_EQ(views::MenuItemView::SUBMENU
, item
->GetType());
264 static_cast<views::MenuDelegate
*>(&delegate
)->ExecuteCommand(id
);
265 EXPECT_EQ(i
, submodel
->last_activation());
266 submodel
->set_last_activation(-1);
269 // Check that selecting the root item is safe. The MenuModel does
270 // not care about the root so MenuModelAdapter should do nothing
271 // (not hit the NOTREACHED check) when the root is selected.
272 static_cast<views::MenuDelegate
*>(&delegate
)->SelectionChanged(menu
);