blob: 22c48ee6512cca8eb27f67e05de79be824f4deea [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/callback.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/test/base/interactive_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/base/view_event_test_base.h"
#include "ui/base/models/menu_model.h"
#include "ui/base/test/ui_controls.h"
#include "ui/views/controls/button/menu_button.h"
#include "ui/views/controls/button/menu_button_listener.h"
#include "ui/views/controls/menu/menu_controller.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_model_adapter.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/menu/submenu_view.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/widget/root_view.h"
#include "ui/views/widget/widget.h"
namespace {
const int kTopMenuBaseId = 100;
const int kSubMenuBaseId = 200;
// Implement most of the ui::MenuModel pure virtual methods for subclasses
//
// Exceptions:
// virtual int GetItemCount() const = 0;
// virtual ItemType GetTypeAt(int index) const = 0;
// virtual int GetCommandIdAt(int index) const = 0;
// virtual string16 GetLabelAt(int index) const = 0;
class CommonMenuModel : public ui::MenuModel {
public:
CommonMenuModel() {
}
virtual ~CommonMenuModel() {
}
protected:
// ui::MenuModel implementation.
virtual bool HasIcons() const OVERRIDE {
return false;
}
virtual bool IsItemDynamicAt(int index) const OVERRIDE {
return false;
}
virtual bool GetAcceleratorAt(int index,
ui::Accelerator* accelerator) const OVERRIDE {
return false;
}
virtual ui::MenuSeparatorType GetSeparatorTypeAt(int index) const OVERRIDE {
return ui::NORMAL_SEPARATOR;
}
virtual bool IsItemCheckedAt(int index) const OVERRIDE {
return false;
}
virtual int GetGroupIdAt(int index) const OVERRIDE {
return 0;
}
virtual bool GetIconAt(int index, gfx::Image* icon) OVERRIDE {
return false;
}
virtual ui::ButtonMenuItemModel* GetButtonMenuItemAt(
int index) const OVERRIDE {
return NULL;
}
virtual bool IsEnabledAt(int index) const OVERRIDE {
return true;
}
virtual ui::MenuModel* GetSubmenuModelAt(int index) const OVERRIDE {
return NULL;
}
virtual void HighlightChangedTo(int index) OVERRIDE {
}
virtual void ActivatedAt(int index) OVERRIDE {
}
virtual void SetMenuModelDelegate(ui::MenuModelDelegate* delegate) OVERRIDE {
}
virtual ui::MenuModelDelegate* GetMenuModelDelegate() const OVERRIDE {
return NULL;
}
private:
DISALLOW_COPY_AND_ASSIGN(CommonMenuModel);
};
class SubMenuModel : public CommonMenuModel {
public:
SubMenuModel()
: showing_(false) {
}
virtual ~SubMenuModel() {
}
bool showing() const {
return showing_;
}
private:
// ui::MenuModel implementation.
virtual int GetItemCount() const OVERRIDE {
return 1;
}
virtual ItemType GetTypeAt(int index) const OVERRIDE {
return TYPE_COMMAND;
}
virtual int GetCommandIdAt(int index) const OVERRIDE {
return index + kSubMenuBaseId;
}
virtual string16 GetLabelAt(int index) const OVERRIDE {
return ASCIIToUTF16("Item");
}
virtual void MenuWillShow() OVERRIDE {
showing_ = true;
}
// Called when the menu has been closed.
virtual void MenuClosed() OVERRIDE {
showing_ = false;
}
bool showing_;
DISALLOW_COPY_AND_ASSIGN(SubMenuModel);
};
class TopMenuModel : public CommonMenuModel {
public:
TopMenuModel() {
}
virtual ~TopMenuModel() {
}
bool IsSubmenuShowing() {
return sub_menu_model_.showing();
}
private:
// ui::MenuModel implementation.
virtual int GetItemCount() const OVERRIDE {
return 1;
}
virtual ItemType GetTypeAt(int index) const OVERRIDE {
return TYPE_SUBMENU;
}
virtual int GetCommandIdAt(int index) const OVERRIDE {
return index + kTopMenuBaseId;
}
virtual string16 GetLabelAt(int index) const OVERRIDE {
return ASCIIToUTF16("submenu");
}
virtual MenuModel* GetSubmenuModelAt(int index) const OVERRIDE {
return &sub_menu_model_;
}
mutable SubMenuModel sub_menu_model_;
DISALLOW_COPY_AND_ASSIGN(TopMenuModel);
};
} // namespace
class MenuModelAdapterTest : public ViewEventTestBase,
public views::MenuButtonListener {
public:
MenuModelAdapterTest()
: ViewEventTestBase(),
button_(NULL),
menu_model_adapter_(&top_menu_model_),
menu_(NULL) {
old_views_delegate_ = views::ViewsDelegate::views_delegate;
views::ViewsDelegate::views_delegate = &views_delegate_;
}
virtual ~MenuModelAdapterTest() {
views::ViewsDelegate::views_delegate = old_views_delegate_;
}
// ViewEventTestBase implementation.
virtual void SetUp() OVERRIDE {
button_ = new views::MenuButton(
NULL, ASCIIToUTF16("Menu Adapter Test"), this, true);
menu_ = menu_model_adapter_.CreateMenu();
menu_runner_.reset(new views::MenuRunner(menu_));
ViewEventTestBase::SetUp();
}
virtual void TearDown() OVERRIDE {
menu_runner_.reset(NULL);
menu_ = NULL;
ViewEventTestBase::TearDown();
}
virtual views::View* CreateContentsView() OVERRIDE {
return button_;
}
virtual gfx::Size GetPreferredSize() OVERRIDE {
return button_->GetPreferredSize();
}
// views::MenuButtonListener implementation.
virtual void OnMenuButtonClicked(views::View* source,
const gfx::Point& point) OVERRIDE {
gfx::Point screen_location;
views::View::ConvertPointToScreen(source, &screen_location);
gfx::Rect bounds(screen_location, source->size());
ignore_result(menu_runner_->RunMenuAt(
source->GetWidget(),
button_,
bounds,
views::MenuItemView::TOPLEFT,
ui::MENU_SOURCE_NONE,
views::MenuRunner::HAS_MNEMONICS));
}
// ViewEventTestBase implementation
virtual void DoTestOnMessageLoop() OVERRIDE {
Click(button_, CreateEventTask(this, &MenuModelAdapterTest::Step1));
}
// Open the submenu.
void Step1() {
views::SubmenuView* topmenu = menu_->GetSubmenu();
ASSERT_TRUE(topmenu);
ASSERT_TRUE(topmenu->IsShowing());
ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
// Click the first item to open the submenu.
views::MenuItemView* item = topmenu->GetMenuItemAt(0);
ASSERT_TRUE(item);
Click(item, CreateEventTask(this, &MenuModelAdapterTest::Step2));
}
// Rebuild the menu which should close the submenu.
void Step2() {
views::SubmenuView* topmenu = menu_->GetSubmenu();
ASSERT_TRUE(topmenu);
ASSERT_TRUE(topmenu->IsShowing());
ASSERT_TRUE(top_menu_model_.IsSubmenuShowing());
menu_model_adapter_.BuildMenu(menu_);
base::MessageLoopForUI::current()->PostTask(
FROM_HERE, CreateEventTask(this, &MenuModelAdapterTest::Step3));
}
// Verify that the submenu MenuModel received the close callback
// and close the menu.
void Step3() {
views::SubmenuView* topmenu = menu_->GetSubmenu();
ASSERT_TRUE(topmenu);
ASSERT_TRUE(topmenu->IsShowing());
ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
// Click the button to exit the menu.
Click(button_, CreateEventTask(this, &MenuModelAdapterTest::Step4));
}
// All done.
void Step4() {
views::SubmenuView* topmenu = menu_->GetSubmenu();
ASSERT_TRUE(topmenu);
ASSERT_FALSE(topmenu->IsShowing());
ASSERT_FALSE(top_menu_model_.IsSubmenuShowing());
Done();
}
private:
// Generate a mouse click on the specified view and post a new task.
virtual void Click(views::View* view, const base::Closure& next) {
ui_test_utils::MoveMouseToCenterAndPress(
view,
ui_controls::LEFT,
ui_controls::DOWN | ui_controls::UP,
next);
}
views::ViewsDelegate* old_views_delegate_;
views::TestViewsDelegate views_delegate_;
views::MenuButton* button_;
TopMenuModel top_menu_model_;
views::MenuModelAdapter menu_model_adapter_;
views::MenuItemView* menu_;
scoped_ptr<views::MenuRunner> menu_runner_;
};
#if defined(OS_WIN)
#define MAYBE_RebuildMenu DISABLED_RebuildMenu
#else
#define MAYBE_RebuildMenu RebuildMenu
#endif
VIEW_TEST(MenuModelAdapterTest, MAYBE_RebuildMenu)