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 virtual ~MenuModelBase() {
31 // ui::MenuModel implementation:
33 virtual bool HasIcons() const OVERRIDE
{
37 virtual int GetItemCount() const OVERRIDE
{
38 return static_cast<int>(items_
.size());
41 virtual ItemType
GetTypeAt(int index
) const OVERRIDE
{
42 return items_
[index
].type
;
45 virtual ui::MenuSeparatorType
GetSeparatorTypeAt(
46 int index
) const OVERRIDE
{
47 return ui::NORMAL_SEPARATOR
;
50 virtual int GetCommandIdAt(int index
) const OVERRIDE
{
51 return index
+ command_id_base_
;
54 virtual base::string16
GetLabelAt(int index
) const OVERRIDE
{
55 return items_
[index
].label
;
58 virtual bool IsItemDynamicAt(int index
) const OVERRIDE
{
62 virtual const gfx::FontList
* GetLabelFontListAt(int index
) const OVERRIDE
{
66 virtual bool GetAcceleratorAt(int index
,
67 ui::Accelerator
* accelerator
) const OVERRIDE
{
71 virtual bool IsItemCheckedAt(int index
) const OVERRIDE
{
75 virtual int GetGroupIdAt(int index
) const OVERRIDE
{
79 virtual bool GetIconAt(int index
, gfx::Image
* icon
) OVERRIDE
{
83 virtual ui::ButtonMenuItemModel
* GetButtonMenuItemAt(
84 int index
) const OVERRIDE
{
88 virtual bool IsEnabledAt(int index
) const OVERRIDE
{
92 virtual bool IsVisibleAt(int index
) const OVERRIDE
{
96 virtual MenuModel
* GetSubmenuModelAt(int index
) const OVERRIDE
{
97 return items_
[index
].submenu
;
100 virtual void HighlightChangedTo(int index
) OVERRIDE
{
103 virtual void ActivatedAt(int index
) OVERRIDE
{
104 set_last_activation(index
);
107 virtual void ActivatedAt(int index
, int event_flags
) OVERRIDE
{
111 virtual void MenuWillShow() OVERRIDE
{
114 virtual void MenuClosed() OVERRIDE
{
117 virtual void SetMenuModelDelegate(
118 ui::MenuModelDelegate
* delegate
) OVERRIDE
{
121 virtual ui::MenuModelDelegate
* GetMenuModelDelegate() const OVERRIDE
{
127 Item(ItemType item_type
,
128 const std::string
& item_label
,
129 ui::MenuModel
* item_submenu
)
131 label(base::ASCIIToUTF16(item_label
)),
132 submenu(item_submenu
) {
136 base::string16 label
;
137 ui::MenuModel
* submenu
;
140 const Item
& GetItemDefinition(int index
) {
141 return items_
[index
];
144 // Access index argument to ActivatedAt().
145 int last_activation() const { return last_activation_
; }
146 void set_last_activation(int last_activation
) {
147 last_activation_
= last_activation
;
151 std::vector
<Item
> items_
;
154 int command_id_base_
;
155 int last_activation_
;
157 DISALLOW_COPY_AND_ASSIGN(MenuModelBase
);
160 class SubmenuModel
: public MenuModelBase
{
162 SubmenuModel() : MenuModelBase(kSubmenuIdBase
) {
163 items_
.push_back(Item(TYPE_COMMAND
, "submenu item 0", NULL
));
164 items_
.push_back(Item(TYPE_COMMAND
, "submenu item 1", NULL
));
167 virtual ~SubmenuModel() {
171 DISALLOW_COPY_AND_ASSIGN(SubmenuModel
);
174 class RootModel
: public MenuModelBase
{
176 RootModel() : MenuModelBase(kRootIdBase
) {
177 submenu_model_
.reset(new SubmenuModel
);
179 items_
.push_back(Item(TYPE_COMMAND
, "command 0", NULL
));
180 items_
.push_back(Item(TYPE_CHECK
, "check 1", NULL
));
181 items_
.push_back(Item(TYPE_SEPARATOR
, "", NULL
));
182 items_
.push_back(Item(TYPE_SUBMENU
, "submenu 3", submenu_model_
.get()));
183 items_
.push_back(Item(TYPE_RADIO
, "radio 4", NULL
));
186 virtual ~RootModel() {
190 scoped_ptr
<MenuModel
> submenu_model_
;
192 DISALLOW_COPY_AND_ASSIGN(RootModel
);
199 typedef ViewsTestBase MenuModelAdapterTest
;
201 TEST_F(MenuModelAdapterTest
, BasicTest
) {
202 // Build model and adapter.
204 views::MenuModelAdapter
delegate(&model
);
206 // Create menu. Build menu twice to check that rebuilding works properly.
207 MenuItemView
* menu
= new views::MenuItemView(&delegate
);
208 // MenuRunner takes ownership of menu.
209 scoped_ptr
<MenuRunner
> menu_runner(new MenuRunner(menu
, 0));
210 delegate
.BuildMenu(menu
);
211 delegate
.BuildMenu(menu
);
212 EXPECT_TRUE(menu
->HasSubmenu());
214 // Check top level menu items.
215 views::SubmenuView
* item_container
= menu
->GetSubmenu();
216 EXPECT_EQ(5, item_container
->child_count());
218 for (int i
= 0; i
< item_container
->child_count(); ++i
) {
219 const MenuModelBase::Item
& model_item
= model
.GetItemDefinition(i
);
221 const int id
= i
+ kRootIdBase
;
222 MenuItemView
* item
= menu
->GetMenuItemByID(id
);
224 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR
, model_item
.type
);
229 EXPECT_EQ(i
, menu
->GetSubmenu()->GetIndexOf(item
));
232 switch (model_item
.type
) {
233 case ui::MenuModel::TYPE_COMMAND
:
234 EXPECT_EQ(views::MenuItemView::NORMAL
, item
->GetType());
236 case ui::MenuModel::TYPE_CHECK
:
237 EXPECT_EQ(views::MenuItemView::CHECKBOX
, item
->GetType());
239 case ui::MenuModel::TYPE_RADIO
:
240 EXPECT_EQ(views::MenuItemView::RADIO
, item
->GetType());
242 case ui::MenuModel::TYPE_SEPARATOR
:
243 case ui::MenuModel::TYPE_BUTTON_ITEM
:
245 case ui::MenuModel::TYPE_SUBMENU
:
246 EXPECT_EQ(views::MenuItemView::SUBMENU
, item
->GetType());
251 static_cast<views::MenuDelegate
*>(&delegate
)->ExecuteCommand(id
);
252 EXPECT_EQ(i
, model
.last_activation());
253 model
.set_last_activation(-1);
256 // Check submenu items.
257 views::MenuItemView
* submenu
= menu
->GetMenuItemByID(103);
258 views::SubmenuView
* subitem_container
= submenu
->GetSubmenu();
259 EXPECT_EQ(2, subitem_container
->child_count());
261 for (int i
= 0; i
< subitem_container
->child_count(); ++i
) {
262 MenuModelBase
* submodel
= static_cast<MenuModelBase
*>(
263 model
.GetSubmenuModelAt(3));
264 EXPECT_TRUE(submodel
);
266 const MenuModelBase::Item
& model_item
= submodel
->GetItemDefinition(i
);
268 const int id
= i
+ kSubmenuIdBase
;
269 MenuItemView
* item
= menu
->GetMenuItemByID(id
);
271 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR
, model_item
.type
);
276 EXPECT_EQ(i
, submenu
->GetSubmenu()->GetIndexOf(item
));
279 switch (model_item
.type
) {
280 case ui::MenuModel::TYPE_COMMAND
:
281 EXPECT_EQ(views::MenuItemView::NORMAL
, item
->GetType());
283 case ui::MenuModel::TYPE_CHECK
:
284 EXPECT_EQ(views::MenuItemView::CHECKBOX
, item
->GetType());
286 case ui::MenuModel::TYPE_RADIO
:
287 EXPECT_EQ(views::MenuItemView::RADIO
, item
->GetType());
289 case ui::MenuModel::TYPE_SEPARATOR
:
290 case ui::MenuModel::TYPE_BUTTON_ITEM
:
292 case ui::MenuModel::TYPE_SUBMENU
:
293 EXPECT_EQ(views::MenuItemView::SUBMENU
, item
->GetType());
298 static_cast<views::MenuDelegate
*>(&delegate
)->ExecuteCommand(id
);
299 EXPECT_EQ(i
, submodel
->last_activation());
300 submodel
->set_last_activation(-1);
303 // Check that selecting the root item is safe. The MenuModel does
304 // not care about the root so MenuModelAdapter should do nothing
305 // (not hit the NOTREACHED check) when the root is selected.
306 static_cast<views::MenuDelegate
*>(&delegate
)->SelectionChanged(menu
);