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