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/l10n/l10n_util.h"
9 #include "ui/base/models/menu_model.h"
10 #include "ui/base/models/menu_model_delegate.h"
11 #include "ui/views/controls/menu/menu_item_view.h"
12 #include "ui/views/controls/menu/menu_runner.h"
13 #include "ui/views/controls/menu/submenu_view.h"
14 #include "ui/views/test/views_test_base.h"
18 // Base command id for test menu and its submenu.
19 const int kRootIdBase
= 100;
20 const int kSubmenuIdBase
= 200;
22 class MenuModelBase
: public ui::MenuModel
{
24 explicit MenuModelBase(int command_id_base
)
25 : command_id_base_(command_id_base
),
26 last_activation_(-1) {
29 virtual ~MenuModelBase() {
32 // ui::MenuModel implementation:
34 virtual bool HasIcons() const OVERRIDE
{
38 virtual int GetItemCount() const OVERRIDE
{
39 return static_cast<int>(items_
.size());
42 virtual ItemType
GetTypeAt(int index
) const OVERRIDE
{
43 return items_
[index
].type
;
46 virtual ui::MenuSeparatorType
GetSeparatorTypeAt(
47 int index
) const OVERRIDE
{
48 return ui::NORMAL_SEPARATOR
;
51 virtual int GetCommandIdAt(int index
) const OVERRIDE
{
52 return index
+ command_id_base_
;
55 virtual base::string16
GetLabelAt(int index
) const OVERRIDE
{
56 return items_
[index
].label
;
59 virtual bool IsItemDynamicAt(int index
) const OVERRIDE
{
63 virtual const gfx::FontList
* GetLabelFontListAt(int index
) const OVERRIDE
{
67 virtual bool GetAcceleratorAt(int index
,
68 ui::Accelerator
* accelerator
) const OVERRIDE
{
72 virtual bool IsItemCheckedAt(int index
) const OVERRIDE
{
76 virtual int GetGroupIdAt(int index
) const OVERRIDE
{
80 virtual bool GetIconAt(int index
, gfx::Image
* icon
) OVERRIDE
{
84 virtual ui::ButtonMenuItemModel
* GetButtonMenuItemAt(
85 int index
) const OVERRIDE
{
89 virtual bool IsEnabledAt(int index
) const OVERRIDE
{
93 virtual bool IsVisibleAt(int index
) const OVERRIDE
{
97 virtual MenuModel
* GetSubmenuModelAt(int index
) const OVERRIDE
{
98 return items_
[index
].submenu
;
101 virtual void HighlightChangedTo(int index
) OVERRIDE
{
104 virtual void ActivatedAt(int index
) OVERRIDE
{
105 set_last_activation(index
);
108 virtual void ActivatedAt(int index
, int event_flags
) OVERRIDE
{
112 virtual void MenuWillShow() OVERRIDE
{
115 virtual void MenuClosed() OVERRIDE
{
118 virtual void SetMenuModelDelegate(
119 ui::MenuModelDelegate
* delegate
) OVERRIDE
{
122 virtual ui::MenuModelDelegate
* GetMenuModelDelegate() const OVERRIDE
{
128 Item(ItemType item_type
,
129 const std::string
& item_label
,
130 ui::MenuModel
* item_submenu
)
132 label(base::ASCIIToUTF16(item_label
)),
133 submenu(item_submenu
) {
137 base::string16 label
;
138 ui::MenuModel
* submenu
;
141 const Item
& GetItemDefinition(int index
) {
142 return items_
[index
];
145 // Access index argument to ActivatedAt().
146 int last_activation() const { return last_activation_
; }
147 void set_last_activation(int last_activation
) {
148 last_activation_
= last_activation
;
152 std::vector
<Item
> items_
;
155 int command_id_base_
;
156 int last_activation_
;
158 DISALLOW_COPY_AND_ASSIGN(MenuModelBase
);
161 class SubmenuModel
: public MenuModelBase
{
163 SubmenuModel() : MenuModelBase(kSubmenuIdBase
) {
164 items_
.push_back(Item(TYPE_COMMAND
, "submenu item 0", NULL
));
165 items_
.push_back(Item(TYPE_COMMAND
, "submenu item 1", NULL
));
168 virtual ~SubmenuModel() {
172 DISALLOW_COPY_AND_ASSIGN(SubmenuModel
);
175 class RootModel
: public MenuModelBase
{
177 RootModel() : MenuModelBase(kRootIdBase
) {
178 submenu_model_
.reset(new SubmenuModel
);
180 items_
.push_back(Item(TYPE_COMMAND
, "command 0", NULL
));
181 items_
.push_back(Item(TYPE_CHECK
, "check 1", NULL
));
182 items_
.push_back(Item(TYPE_SEPARATOR
, "", NULL
));
183 items_
.push_back(Item(TYPE_SUBMENU
, "submenu 3", submenu_model_
.get()));
184 items_
.push_back(Item(TYPE_RADIO
, "radio 4", NULL
));
187 virtual ~RootModel() {
191 scoped_ptr
<MenuModel
> submenu_model_
;
193 DISALLOW_COPY_AND_ASSIGN(RootModel
);
200 typedef ViewsTestBase MenuModelAdapterTest
;
202 TEST_F(MenuModelAdapterTest
, BasicTest
) {
203 // Build model and adapter.
205 views::MenuModelAdapter
delegate(&model
);
207 // Create menu. Build menu twice to check that rebuilding works properly.
208 MenuItemView
* menu
= new views::MenuItemView(&delegate
);
209 // MenuRunner takes ownership of menu.
210 scoped_ptr
<MenuRunner
> menu_runner(new MenuRunner(menu
));
211 delegate
.BuildMenu(menu
);
212 delegate
.BuildMenu(menu
);
213 EXPECT_TRUE(menu
->HasSubmenu());
215 // Check top level menu items.
216 views::SubmenuView
* item_container
= menu
->GetSubmenu();
217 EXPECT_EQ(5, item_container
->child_count());
219 for (int i
= 0; i
< item_container
->child_count(); ++i
) {
220 const MenuModelBase::Item
& model_item
= model
.GetItemDefinition(i
);
222 const int id
= i
+ kRootIdBase
;
223 MenuItemView
* item
= menu
->GetMenuItemByID(id
);
225 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR
, model_item
.type
);
230 EXPECT_EQ(i
, menu
->GetSubmenu()->GetIndexOf(item
));
233 switch (model_item
.type
) {
234 case ui::MenuModel::TYPE_COMMAND
:
235 EXPECT_EQ(views::MenuItemView::NORMAL
, item
->GetType());
237 case ui::MenuModel::TYPE_CHECK
:
238 EXPECT_EQ(views::MenuItemView::CHECKBOX
, item
->GetType());
240 case ui::MenuModel::TYPE_RADIO
:
241 EXPECT_EQ(views::MenuItemView::RADIO
, item
->GetType());
243 case ui::MenuModel::TYPE_SEPARATOR
:
244 case ui::MenuModel::TYPE_BUTTON_ITEM
:
246 case ui::MenuModel::TYPE_SUBMENU
:
247 EXPECT_EQ(views::MenuItemView::SUBMENU
, item
->GetType());
252 static_cast<views::MenuDelegate
*>(&delegate
)->ExecuteCommand(id
);
253 EXPECT_EQ(i
, model
.last_activation());
254 model
.set_last_activation(-1);
257 // Check submenu items.
258 views::MenuItemView
* submenu
= menu
->GetMenuItemByID(103);
259 views::SubmenuView
* subitem_container
= submenu
->GetSubmenu();
260 EXPECT_EQ(2, subitem_container
->child_count());
262 for (int i
= 0; i
< subitem_container
->child_count(); ++i
) {
263 MenuModelBase
* submodel
= static_cast<MenuModelBase
*>(
264 model
.GetSubmenuModelAt(3));
265 EXPECT_TRUE(submodel
);
267 const MenuModelBase::Item
& model_item
= submodel
->GetItemDefinition(i
);
269 const int id
= i
+ kSubmenuIdBase
;
270 MenuItemView
* item
= menu
->GetMenuItemByID(id
);
272 EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR
, model_item
.type
);
277 EXPECT_EQ(i
, submenu
->GetSubmenu()->GetIndexOf(item
));
280 switch (model_item
.type
) {
281 case ui::MenuModel::TYPE_COMMAND
:
282 EXPECT_EQ(views::MenuItemView::NORMAL
, item
->GetType());
284 case ui::MenuModel::TYPE_CHECK
:
285 EXPECT_EQ(views::MenuItemView::CHECKBOX
, item
->GetType());
287 case ui::MenuModel::TYPE_RADIO
:
288 EXPECT_EQ(views::MenuItemView::RADIO
, item
->GetType());
290 case ui::MenuModel::TYPE_SEPARATOR
:
291 case ui::MenuModel::TYPE_BUTTON_ITEM
:
293 case ui::MenuModel::TYPE_SUBMENU
:
294 EXPECT_EQ(views::MenuItemView::SUBMENU
, item
->GetType());
299 static_cast<views::MenuDelegate
*>(&delegate
)->ExecuteCommand(id
);
300 EXPECT_EQ(i
, submodel
->last_activation());
301 submodel
->set_last_activation(-1);
304 // Check that selecting the root item is safe. The MenuModel does
305 // not care about the root so MenuModelAdapter should do nothing
306 // (not hit the NOTREACHED check) when the root is selected.
307 static_cast<views::MenuDelegate
*>(&delegate
)->SelectionChanged(menu
);