menu_model_adapter_test.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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.
4
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"
21
22namespace {
23
24const int kTopMenuBaseId = 100;
25const int kSubMenuBaseId = 200;
26
27// Implement most of the ui::MenuModel pure virtual methods for subclasses
28//
29// Exceptions:
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;
34class CommonMenuModel : public ui::MenuModel {
35 public:
36  CommonMenuModel() {
37  }
38
39  virtual ~CommonMenuModel() {
40  }
41
42 protected:
43  // ui::MenuModel implementation.
44  virtual bool HasIcons() const OVERRIDE {
45    return false;
46  }
47
48  virtual bool IsItemDynamicAt(int index) const OVERRIDE {
49    return false;
50  }
51
52  virtual bool GetAcceleratorAt(int index,
53                                ui::Accelerator* accelerator) const OVERRIDE {
54    return false;
55  }
56
57  virtual ui::MenuSeparatorType GetSeparatorTypeAt(int index) const OVERRIDE {
58    return ui::NORMAL_SEPARATOR;
59  }
60
61  virtual bool IsItemCheckedAt(int index) const OVERRIDE {
62    return false;
63  }
64
65  virtual int GetGroupIdAt(int index) const OVERRIDE {
66    return 0;
67  }
68
69  virtual bool GetIconAt(int index, gfx::Image* icon) OVERRIDE {
70    return false;
71  }
72
73  virtual ui::ButtonMenuItemModel* GetButtonMenuItemAt(
74      int index) const OVERRIDE {
75    return NULL;
76  }
77
78  virtual bool IsEnabledAt(int index) const OVERRIDE {
79    return true;
80  }
81
82  virtual ui::MenuModel* GetSubmenuModelAt(int index) const OVERRIDE {
83    return NULL;
84  }
85
86  virtual void HighlightChangedTo(int index) OVERRIDE {
87  }
88
89  virtual void ActivatedAt(int index) OVERRIDE {
90  }
91
92  virtual void SetMenuModelDelegate(ui::MenuModelDelegate* delegate) OVERRIDE {
93  }
94
95  virtual ui::MenuModelDelegate* GetMenuModelDelegate() const OVERRIDE {
96    return NULL;
97  }
98
99 private:
100  DISALLOW_COPY_AND_ASSIGN(CommonMenuModel);
101};
102
103class SubMenuModel : public CommonMenuModel {
104 public:
105  SubMenuModel()
106      : showing_(false) {
107  }
108
109  virtual ~SubMenuModel() {
110  }
111
112  bool showing() const {
113    return showing_;
114  }
115
116 private:
117  // ui::MenuModel implementation.
118  virtual int GetItemCount() const OVERRIDE {
119    return 1;
120  }
121
122  virtual ItemType GetTypeAt(int index) const OVERRIDE {
123    return TYPE_COMMAND;
124  }
125
126  virtual int GetCommandIdAt(int index) const OVERRIDE {
127    return index + kSubMenuBaseId;
128  }
129
130  virtual base::string16 GetLabelAt(int index) const OVERRIDE {
131    return base::ASCIIToUTF16("Item");
132  }
133
134  virtual void MenuWillShow() OVERRIDE {
135    showing_ = true;
136  }
137
138  // Called when the menu has been closed.
139  virtual void MenuClosed() OVERRIDE {
140    showing_ = false;
141  }
142
143  bool showing_;
144
145  DISALLOW_COPY_AND_ASSIGN(SubMenuModel);
146};
147
148class TopMenuModel : public CommonMenuModel {
149 public:
150  TopMenuModel() {
151  }
152
153  virtual ~TopMenuModel() {
154  }
155
156  bool IsSubmenuShowing() {
157    return sub_menu_model_.showing();
158  }
159
160 private:
161  // ui::MenuModel implementation.
162  virtual int GetItemCount() const OVERRIDE {
163    return 1;
164  }
165
166  virtual ItemType GetTypeAt(int index) const OVERRIDE {
167    return TYPE_SUBMENU;
168  }
169
170  virtual int GetCommandIdAt(int index) const OVERRIDE {
171    return index + kTopMenuBaseId;
172  }
173
174  virtual base::string16 GetLabelAt(int index) const OVERRIDE {
175    return base::ASCIIToUTF16("submenu");
176  }
177
178  virtual MenuModel* GetSubmenuModelAt(int index) const OVERRIDE {
179    return &sub_menu_model_;
180  }
181
182  mutable SubMenuModel sub_menu_model_;
183
184  DISALLOW_COPY_AND_ASSIGN(TopMenuModel);
185};
186
187}  // namespace
188
189class MenuModelAdapterTest : public ViewEventTestBase,
190                             public views::MenuButtonListener {
191 public:
192  MenuModelAdapterTest()
193      : ViewEventTestBase(),
194        button_(NULL),
195        menu_model_adapter_(&top_menu_model_),
196        menu_(NULL) {
197  }
198
199  virtual ~MenuModelAdapterTest() {
200  }
201
202  // ViewEventTestBase implementation.
203
204  virtual void SetUp() OVERRIDE {
205    button_ = new views::MenuButton(
206        NULL, base::ASCIIToUTF16("Menu Adapter Test"), this, true);
207
208    menu_ = menu_model_adapter_.CreateMenu();
209    menu_runner_.reset(
210        new views::MenuRunner(menu_, views::MenuRunner::HAS_MNEMONICS));
211
212    ViewEventTestBase::SetUp();
213  }
214
215  virtual void TearDown() OVERRIDE {
216    menu_runner_.reset(NULL);
217    menu_ = NULL;
218    ViewEventTestBase::TearDown();
219  }
220
221  virtual views::View* CreateContentsView() OVERRIDE {
222    return button_;
223  }
224
225  virtual gfx::Size GetPreferredSize() const OVERRIDE {
226    return button_->GetPreferredSize();
227  }
228
229  // views::MenuButtonListener implementation.
230  virtual void OnMenuButtonClicked(views::View* source,
231                                   const gfx::Point& point) OVERRIDE {
232    gfx::Point screen_location;
233    views::View::ConvertPointToScreen(source, &screen_location);
234    gfx::Rect bounds(screen_location, source->size());
235    ignore_result(menu_runner_->RunMenuAt(source->GetWidget(),
236                                          button_,
237                                          bounds,
238                                          views::MENU_ANCHOR_TOPLEFT,
239                                          ui::MENU_SOURCE_NONE));
240  }
241
242  // ViewEventTestBase implementation
243  virtual void DoTestOnMessageLoop() OVERRIDE {
244    Click(button_, CreateEventTask(this, &MenuModelAdapterTest::Step1));
245  }
246
247  // Open the submenu.
248  void Step1() {
249    views::SubmenuView* topmenu = menu_->GetSubmenu();
250    ASSERT_TRUE(topmenu);
251    ASSERT_TRUE(topmenu->IsShowing());
252    ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
253
254    // Click the first item to open the submenu.
255    views::MenuItemView* item = topmenu->GetMenuItemAt(0);
256    ASSERT_TRUE(item);
257    Click(item, CreateEventTask(this, &MenuModelAdapterTest::Step2));
258  }
259
260  // Rebuild the menu which should close the submenu.
261  void Step2() {
262    views::SubmenuView* topmenu = menu_->GetSubmenu();
263    ASSERT_TRUE(topmenu);
264    ASSERT_TRUE(topmenu->IsShowing());
265    ASSERT_TRUE(top_menu_model_.IsSubmenuShowing());
266
267    menu_model_adapter_.BuildMenu(menu_);
268
269    base::MessageLoopForUI::current()->PostTask(
270        FROM_HERE, CreateEventTask(this, &MenuModelAdapterTest::Step3));
271  }
272
273  // Verify that the submenu MenuModel received the close callback
274  // and close the menu.
275  void Step3() {
276    views::SubmenuView* topmenu = menu_->GetSubmenu();
277    ASSERT_TRUE(topmenu);
278    ASSERT_TRUE(topmenu->IsShowing());
279    ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
280
281    // Click the button to exit the menu.
282    Click(button_, CreateEventTask(this, &MenuModelAdapterTest::Step4));
283  }
284
285  // All done.
286  void Step4() {
287    views::SubmenuView* topmenu = menu_->GetSubmenu();
288    ASSERT_TRUE(topmenu);
289    ASSERT_FALSE(topmenu->IsShowing());
290    ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
291
292    Done();
293  }
294
295 private:
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(
299        view,
300        ui_controls::LEFT,
301        ui_controls::DOWN | ui_controls::UP,
302        next);
303  }
304
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_;
310};
311
312VIEW_TEST(MenuModelAdapterTest, RebuildMenu)
313