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