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 virtual ~CommonMenuModel() {
43 // ui::MenuModel implementation.
44 virtual bool HasIcons() const OVERRIDE
{
48 virtual bool IsItemDynamicAt(int index
) const OVERRIDE
{
52 virtual bool GetAcceleratorAt(int index
,
53 ui::Accelerator
* accelerator
) const OVERRIDE
{
57 virtual ui::MenuSeparatorType
GetSeparatorTypeAt(int index
) const OVERRIDE
{
58 return ui::NORMAL_SEPARATOR
;
61 virtual bool IsItemCheckedAt(int index
) const OVERRIDE
{
65 virtual int GetGroupIdAt(int index
) const OVERRIDE
{
69 virtual bool GetIconAt(int index
, gfx::Image
* icon
) OVERRIDE
{
73 virtual ui::ButtonMenuItemModel
* GetButtonMenuItemAt(
74 int index
) const OVERRIDE
{
78 virtual bool IsEnabledAt(int index
) const OVERRIDE
{
82 virtual ui::MenuModel
* GetSubmenuModelAt(int index
) const OVERRIDE
{
86 virtual void HighlightChangedTo(int index
) OVERRIDE
{
89 virtual void ActivatedAt(int index
) OVERRIDE
{
92 virtual void SetMenuModelDelegate(ui::MenuModelDelegate
* delegate
) OVERRIDE
{
95 virtual ui::MenuModelDelegate
* GetMenuModelDelegate() const OVERRIDE
{
100 DISALLOW_COPY_AND_ASSIGN(CommonMenuModel
);
103 class SubMenuModel
: public CommonMenuModel
{
109 virtual ~SubMenuModel() {
112 bool showing() const {
117 // ui::MenuModel implementation.
118 virtual int GetItemCount() const OVERRIDE
{
122 virtual ItemType
GetTypeAt(int index
) const OVERRIDE
{
126 virtual int GetCommandIdAt(int index
) const OVERRIDE
{
127 return index
+ kSubMenuBaseId
;
130 virtual base::string16
GetLabelAt(int index
) const OVERRIDE
{
131 return base::ASCIIToUTF16("Item");
134 virtual void MenuWillShow() OVERRIDE
{
138 // Called when the menu has been closed.
139 virtual void MenuClosed() OVERRIDE
{
145 DISALLOW_COPY_AND_ASSIGN(SubMenuModel
);
148 class TopMenuModel
: public CommonMenuModel
{
153 virtual ~TopMenuModel() {
156 bool IsSubmenuShowing() {
157 return sub_menu_model_
.showing();
161 // ui::MenuModel implementation.
162 virtual int GetItemCount() const OVERRIDE
{
166 virtual ItemType
GetTypeAt(int index
) const OVERRIDE
{
170 virtual int GetCommandIdAt(int index
) const OVERRIDE
{
171 return index
+ kTopMenuBaseId
;
174 virtual base::string16
GetLabelAt(int index
) const OVERRIDE
{
175 return base::ASCIIToUTF16("submenu");
178 virtual MenuModel
* GetSubmenuModelAt(int index
) const OVERRIDE
{
179 return &sub_menu_model_
;
182 mutable SubMenuModel sub_menu_model_
;
184 DISALLOW_COPY_AND_ASSIGN(TopMenuModel
);
189 class MenuModelAdapterTest
: public ViewEventTestBase
,
190 public views::MenuButtonListener
{
192 MenuModelAdapterTest()
193 : ViewEventTestBase(),
195 menu_model_adapter_(&top_menu_model_
),
199 virtual ~MenuModelAdapterTest() {
202 // ViewEventTestBase implementation.
204 virtual void SetUp() OVERRIDE
{
205 button_
= new views::MenuButton(
206 NULL
, base::ASCIIToUTF16("Menu Adapter Test"), this, true);
208 menu_
= menu_model_adapter_
.CreateMenu();
209 menu_runner_
.reset(new views::MenuRunner(menu_
));
211 ViewEventTestBase::SetUp();
214 virtual void TearDown() OVERRIDE
{
215 menu_runner_
.reset(NULL
);
217 ViewEventTestBase::TearDown();
220 virtual views::View
* CreateContentsView() OVERRIDE
{
224 virtual gfx::Size
GetPreferredSize() const OVERRIDE
{
225 return button_
->GetPreferredSize();
228 // views::MenuButtonListener implementation.
229 virtual void OnMenuButtonClicked(views::View
* source
,
230 const gfx::Point
& point
) OVERRIDE
{
231 gfx::Point screen_location
;
232 views::View::ConvertPointToScreen(source
, &screen_location
);
233 gfx::Rect
bounds(screen_location
, source
->size());
234 ignore_result(menu_runner_
->RunMenuAt(source
->GetWidget(),
237 views::MENU_ANCHOR_TOPLEFT
,
238 ui::MENU_SOURCE_NONE
,
239 views::MenuRunner::HAS_MNEMONICS
));
242 // ViewEventTestBase implementation
243 virtual void DoTestOnMessageLoop() OVERRIDE
{
244 Click(button_
, CreateEventTask(this, &MenuModelAdapterTest::Step1
));
249 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
250 ASSERT_TRUE(topmenu
);
251 ASSERT_TRUE(topmenu
->IsShowing());
252 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
254 // Click the first item to open the submenu.
255 views::MenuItemView
* item
= topmenu
->GetMenuItemAt(0);
257 Click(item
, CreateEventTask(this, &MenuModelAdapterTest::Step2
));
260 // Rebuild the menu which should close the submenu.
262 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
263 ASSERT_TRUE(topmenu
);
264 ASSERT_TRUE(topmenu
->IsShowing());
265 ASSERT_TRUE(top_menu_model_
.IsSubmenuShowing());
267 menu_model_adapter_
.BuildMenu(menu_
);
269 base::MessageLoopForUI::current()->PostTask(
270 FROM_HERE
, CreateEventTask(this, &MenuModelAdapterTest::Step3
));
273 // Verify that the submenu MenuModel received the close callback
274 // and close the menu.
276 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
277 ASSERT_TRUE(topmenu
);
278 ASSERT_TRUE(topmenu
->IsShowing());
279 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
281 // Click the button to exit the menu.
282 Click(button_
, CreateEventTask(this, &MenuModelAdapterTest::Step4
));
287 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
288 ASSERT_TRUE(topmenu
);
289 ASSERT_FALSE(topmenu
->IsShowing());
290 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
296 // Generate a mouse click on the specified view and post a new task.
297 virtual void Click(views::View
* view
, const base::Closure
& next
) {
298 ui_test_utils::MoveMouseToCenterAndPress(
301 ui_controls::DOWN
| ui_controls::UP
,
305 views::MenuButton
* button_
;
306 TopMenuModel top_menu_model_
;
307 views::MenuModelAdapter menu_model_adapter_
;
308 views::MenuItemView
* menu_
;
309 scoped_ptr
<views::MenuRunner
> menu_runner_
;
312 VIEW_TEST(MenuModelAdapterTest
, RebuildMenu
)