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 "base/callback.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "chrome/test/base/interactive_test_utils.h"
8 #include "chrome/test/base/ui_test_utils.h"
9 #include "chrome/test/base/view_event_test_base.h"
10 #include "ui/base/models/menu_model.h"
11 #include "ui/base/test/ui_controls.h"
12 #include "ui/views/controls/button/menu_button.h"
13 #include "ui/views/controls/button/menu_button_listener.h"
14 #include "ui/views/controls/menu/menu_controller.h"
15 #include "ui/views/controls/menu/menu_item_view.h"
16 #include "ui/views/controls/menu/menu_model_adapter.h"
17 #include "ui/views/controls/menu/menu_runner.h"
18 #include "ui/views/controls/menu/submenu_view.h"
19 #include "ui/views/widget/root_view.h"
20 #include "ui/views/widget/widget.h"
24 const int kTopMenuBaseId
= 100;
25 const int kSubMenuBaseId
= 200;
27 // Implement most of the ui::MenuModel pure virtual methods for subclasses
30 // virtual int GetItemCount() const = 0;
31 // virtual ItemType GetTypeAt(int index) const = 0;
32 // virtual int GetCommandIdAt(int index) const = 0;
33 // virtual base::string16 GetLabelAt(int index) const = 0;
34 class CommonMenuModel
: public ui::MenuModel
{
39 ~CommonMenuModel() override
{}
42 // ui::MenuModel implementation.
43 bool HasIcons() const override
{ return false; }
45 bool IsItemDynamicAt(int index
) const override
{ return false; }
47 bool GetAcceleratorAt(int index
,
48 ui::Accelerator
* accelerator
) const override
{
52 ui::MenuSeparatorType
GetSeparatorTypeAt(int index
) const override
{
53 return ui::NORMAL_SEPARATOR
;
56 bool IsItemCheckedAt(int index
) const override
{ return false; }
58 int GetGroupIdAt(int index
) const override
{ return 0; }
60 bool GetIconAt(int index
, gfx::Image
* icon
) override
{ return false; }
62 ui::ButtonMenuItemModel
* GetButtonMenuItemAt(int index
) const override
{
66 bool IsEnabledAt(int index
) const override
{ return true; }
68 ui::MenuModel
* GetSubmenuModelAt(int index
) const override
{ return NULL
; }
70 void HighlightChangedTo(int index
) override
{}
72 void ActivatedAt(int index
) override
{}
74 void SetMenuModelDelegate(ui::MenuModelDelegate
* delegate
) override
{}
76 ui::MenuModelDelegate
* GetMenuModelDelegate() const override
{ return NULL
; }
79 DISALLOW_COPY_AND_ASSIGN(CommonMenuModel
);
82 class SubMenuModel
: public CommonMenuModel
{
88 ~SubMenuModel() override
{}
90 bool showing() const {
95 // ui::MenuModel implementation.
96 int GetItemCount() const override
{ return 1; }
98 ItemType
GetTypeAt(int index
) const override
{ return TYPE_COMMAND
; }
100 int GetCommandIdAt(int index
) const override
{
101 return index
+ kSubMenuBaseId
;
104 base::string16
GetLabelAt(int index
) const override
{
105 return base::ASCIIToUTF16("Item");
108 void MenuWillShow() override
{ showing_
= true; }
110 // Called when the menu has been closed.
111 void MenuClosed() override
{ showing_
= false; }
115 DISALLOW_COPY_AND_ASSIGN(SubMenuModel
);
118 class TopMenuModel
: public CommonMenuModel
{
123 ~TopMenuModel() override
{}
125 bool IsSubmenuShowing() {
126 return sub_menu_model_
.showing();
130 // ui::MenuModel implementation.
131 int GetItemCount() const override
{ return 1; }
133 ItemType
GetTypeAt(int index
) const override
{ return TYPE_SUBMENU
; }
135 int GetCommandIdAt(int index
) const override
{
136 return index
+ kTopMenuBaseId
;
139 base::string16
GetLabelAt(int index
) const override
{
140 return base::ASCIIToUTF16("submenu");
143 MenuModel
* GetSubmenuModelAt(int index
) const override
{
144 return &sub_menu_model_
;
147 mutable SubMenuModel sub_menu_model_
;
149 DISALLOW_COPY_AND_ASSIGN(TopMenuModel
);
154 class MenuModelAdapterTest
: public ViewEventTestBase
,
155 public views::MenuButtonListener
{
157 MenuModelAdapterTest()
158 : ViewEventTestBase(),
160 menu_model_adapter_(&top_menu_model_
),
164 ~MenuModelAdapterTest() override
{}
166 // ViewEventTestBase implementation.
168 void SetUp() override
{
169 button_
= new views::MenuButton(
170 NULL
, base::ASCIIToUTF16("Menu Adapter Test"), this, true);
172 menu_
= menu_model_adapter_
.CreateMenu();
174 new views::MenuRunner(menu_
, views::MenuRunner::HAS_MNEMONICS
));
176 ViewEventTestBase::SetUp();
179 void TearDown() override
{
180 menu_runner_
.reset(NULL
);
182 ViewEventTestBase::TearDown();
185 views::View
* CreateContentsView() override
{ return button_
; }
187 gfx::Size
GetPreferredSize() const override
{
188 return button_
->GetPreferredSize();
191 // views::MenuButtonListener implementation.
192 void OnMenuButtonClicked(views::View
* source
,
193 const gfx::Point
& point
) override
{
194 gfx::Point screen_location
;
195 views::View::ConvertPointToScreen(source
, &screen_location
);
196 gfx::Rect
bounds(screen_location
, source
->size());
197 ignore_result(menu_runner_
->RunMenuAt(source
->GetWidget(),
200 views::MENU_ANCHOR_TOPLEFT
,
201 ui::MENU_SOURCE_NONE
));
204 // ViewEventTestBase implementation
205 void DoTestOnMessageLoop() override
{
206 Click(button_
, CreateEventTask(this, &MenuModelAdapterTest::Step1
));
211 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
212 ASSERT_TRUE(topmenu
);
213 ASSERT_TRUE(topmenu
->IsShowing());
214 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
216 // Click the first item to open the submenu.
217 views::MenuItemView
* item
= topmenu
->GetMenuItemAt(0);
219 Click(item
, CreateEventTask(this, &MenuModelAdapterTest::Step2
));
222 // Rebuild the menu which should close the submenu.
224 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
225 ASSERT_TRUE(topmenu
);
226 ASSERT_TRUE(topmenu
->IsShowing());
227 ASSERT_TRUE(top_menu_model_
.IsSubmenuShowing());
229 menu_model_adapter_
.BuildMenu(menu_
);
231 base::MessageLoopForUI::current()->PostTask(
232 FROM_HERE
, CreateEventTask(this, &MenuModelAdapterTest::Step3
));
235 // Verify that the submenu MenuModel received the close callback
236 // and close the menu.
238 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
239 ASSERT_TRUE(topmenu
);
240 ASSERT_TRUE(topmenu
->IsShowing());
241 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
243 // Click the button to exit the menu.
244 Click(button_
, CreateEventTask(this, &MenuModelAdapterTest::Step4
));
249 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
250 ASSERT_TRUE(topmenu
);
251 ASSERT_FALSE(topmenu
->IsShowing());
252 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
258 // Generate a mouse click on the specified view and post a new task.
259 virtual void Click(views::View
* view
, const base::Closure
& next
) {
260 ui_test_utils::MoveMouseToCenterAndPress(
263 ui_controls::DOWN
| ui_controls::UP
,
267 views::MenuButton
* button_
;
268 TopMenuModel top_menu_model_
;
269 views::MenuModelAdapter menu_model_adapter_
;
270 views::MenuItemView
* menu_
;
271 scoped_ptr
<views::MenuRunner
> menu_runner_
;
274 VIEW_TEST(MenuModelAdapterTest
, RebuildMenu
)