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/test/test_views_delegate.h"
20 #include "ui/views/widget/root_view.h"
21 #include "ui/views/widget/widget.h"
25 const int kTopMenuBaseId
= 100;
26 const int kSubMenuBaseId
= 200;
28 // Implement most of the ui::MenuModel pure virtual methods for subclasses
31 // virtual int GetItemCount() const = 0;
32 // virtual ItemType GetTypeAt(int index) const = 0;
33 // virtual int GetCommandIdAt(int index) const = 0;
34 // virtual base::string16 GetLabelAt(int index) const = 0;
35 class CommonMenuModel
: public ui::MenuModel
{
40 virtual ~CommonMenuModel() {
44 // ui::MenuModel implementation.
45 virtual bool HasIcons() const OVERRIDE
{
49 virtual bool IsItemDynamicAt(int index
) const OVERRIDE
{
53 virtual bool GetAcceleratorAt(int index
,
54 ui::Accelerator
* accelerator
) const OVERRIDE
{
58 virtual ui::MenuSeparatorType
GetSeparatorTypeAt(int index
) const OVERRIDE
{
59 return ui::NORMAL_SEPARATOR
;
62 virtual bool IsItemCheckedAt(int index
) const OVERRIDE
{
66 virtual int GetGroupIdAt(int index
) const OVERRIDE
{
70 virtual bool GetIconAt(int index
, gfx::Image
* icon
) OVERRIDE
{
74 virtual ui::ButtonMenuItemModel
* GetButtonMenuItemAt(
75 int index
) const OVERRIDE
{
79 virtual bool IsEnabledAt(int index
) const OVERRIDE
{
83 virtual ui::MenuModel
* GetSubmenuModelAt(int index
) const OVERRIDE
{
87 virtual void HighlightChangedTo(int index
) OVERRIDE
{
90 virtual void ActivatedAt(int index
) OVERRIDE
{
93 virtual void SetMenuModelDelegate(ui::MenuModelDelegate
* delegate
) OVERRIDE
{
96 virtual ui::MenuModelDelegate
* GetMenuModelDelegate() const OVERRIDE
{
101 DISALLOW_COPY_AND_ASSIGN(CommonMenuModel
);
104 class SubMenuModel
: public CommonMenuModel
{
110 virtual ~SubMenuModel() {
113 bool showing() const {
118 // ui::MenuModel implementation.
119 virtual int GetItemCount() const OVERRIDE
{
123 virtual ItemType
GetTypeAt(int index
) const OVERRIDE
{
127 virtual int GetCommandIdAt(int index
) const OVERRIDE
{
128 return index
+ kSubMenuBaseId
;
131 virtual base::string16
GetLabelAt(int index
) const OVERRIDE
{
132 return base::ASCIIToUTF16("Item");
135 virtual void MenuWillShow() OVERRIDE
{
139 // Called when the menu has been closed.
140 virtual void MenuClosed() OVERRIDE
{
146 DISALLOW_COPY_AND_ASSIGN(SubMenuModel
);
149 class TopMenuModel
: public CommonMenuModel
{
154 virtual ~TopMenuModel() {
157 bool IsSubmenuShowing() {
158 return sub_menu_model_
.showing();
162 // ui::MenuModel implementation.
163 virtual int GetItemCount() const OVERRIDE
{
167 virtual ItemType
GetTypeAt(int index
) const OVERRIDE
{
171 virtual int GetCommandIdAt(int index
) const OVERRIDE
{
172 return index
+ kTopMenuBaseId
;
175 virtual base::string16
GetLabelAt(int index
) const OVERRIDE
{
176 return base::ASCIIToUTF16("submenu");
179 virtual MenuModel
* GetSubmenuModelAt(int index
) const OVERRIDE
{
180 return &sub_menu_model_
;
183 mutable SubMenuModel sub_menu_model_
;
185 DISALLOW_COPY_AND_ASSIGN(TopMenuModel
);
190 class MenuModelAdapterTest
: public ViewEventTestBase
,
191 public views::MenuButtonListener
{
193 MenuModelAdapterTest()
194 : ViewEventTestBase(),
196 menu_model_adapter_(&top_menu_model_
),
198 old_views_delegate_
= views::ViewsDelegate::views_delegate
;
199 views::ViewsDelegate::views_delegate
= &views_delegate_
;
202 virtual ~MenuModelAdapterTest() {
203 views::ViewsDelegate::views_delegate
= old_views_delegate_
;
206 // ViewEventTestBase implementation.
208 virtual void SetUp() OVERRIDE
{
209 button_
= new views::MenuButton(
210 NULL
, base::ASCIIToUTF16("Menu Adapter Test"), this, true);
212 menu_
= menu_model_adapter_
.CreateMenu();
213 menu_runner_
.reset(new views::MenuRunner(menu_
));
215 ViewEventTestBase::SetUp();
218 virtual void TearDown() OVERRIDE
{
219 menu_runner_
.reset(NULL
);
221 ViewEventTestBase::TearDown();
224 virtual views::View
* CreateContentsView() OVERRIDE
{
228 virtual gfx::Size
GetPreferredSize() OVERRIDE
{
229 return button_
->GetPreferredSize();
232 // views::MenuButtonListener implementation.
233 virtual void OnMenuButtonClicked(views::View
* source
,
234 const gfx::Point
& point
) OVERRIDE
{
235 gfx::Point screen_location
;
236 views::View::ConvertPointToScreen(source
, &screen_location
);
237 gfx::Rect
bounds(screen_location
, source
->size());
238 ignore_result(menu_runner_
->RunMenuAt(
242 views::MenuItemView::TOPLEFT
,
243 ui::MENU_SOURCE_NONE
,
244 views::MenuRunner::HAS_MNEMONICS
));
247 // ViewEventTestBase implementation
248 virtual void DoTestOnMessageLoop() OVERRIDE
{
249 Click(button_
, CreateEventTask(this, &MenuModelAdapterTest::Step1
));
254 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
255 ASSERT_TRUE(topmenu
);
256 ASSERT_TRUE(topmenu
->IsShowing());
257 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
259 // Click the first item to open the submenu.
260 views::MenuItemView
* item
= topmenu
->GetMenuItemAt(0);
262 Click(item
, CreateEventTask(this, &MenuModelAdapterTest::Step2
));
265 // Rebuild the menu which should close the submenu.
267 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
268 ASSERT_TRUE(topmenu
);
269 ASSERT_TRUE(topmenu
->IsShowing());
270 ASSERT_TRUE(top_menu_model_
.IsSubmenuShowing());
272 menu_model_adapter_
.BuildMenu(menu_
);
274 base::MessageLoopForUI::current()->PostTask(
275 FROM_HERE
, CreateEventTask(this, &MenuModelAdapterTest::Step3
));
278 // Verify that the submenu MenuModel received the close callback
279 // and close the menu.
281 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
282 ASSERT_TRUE(topmenu
);
283 ASSERT_TRUE(topmenu
->IsShowing());
284 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
286 // Click the button to exit the menu.
287 Click(button_
, CreateEventTask(this, &MenuModelAdapterTest::Step4
));
292 views::SubmenuView
* topmenu
= menu_
->GetSubmenu();
293 ASSERT_TRUE(topmenu
);
294 ASSERT_FALSE(topmenu
->IsShowing());
295 ASSERT_FALSE(top_menu_model_
.IsSubmenuShowing());
301 // Generate a mouse click on the specified view and post a new task.
302 virtual void Click(views::View
* view
, const base::Closure
& next
) {
303 ui_test_utils::MoveMouseToCenterAndPress(
306 ui_controls::DOWN
| ui_controls::UP
,
310 views::ViewsDelegate
* old_views_delegate_
;
311 views::TestViewsDelegate views_delegate_
;
313 views::MenuButton
* button_
;
314 TopMenuModel top_menu_model_
;
315 views::MenuModelAdapter menu_model_adapter_
;
316 views::MenuItemView
* menu_
;
317 scoped_ptr
<views::MenuRunner
> menu_runner_
;
320 VIEW_TEST(MenuModelAdapterTest
, RebuildMenu
)