Home | History | Annotate | Download | only in views
      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 
     22 namespace {
     23 
     24 const int kTopMenuBaseId = 100;
     25 const 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;
     34 class 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 
    103 class 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 
    148 class 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 
    189 class 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(new views::MenuRunner(menu_));
    210 
    211     ViewEventTestBase::SetUp();
    212   }
    213 
    214   virtual void TearDown() OVERRIDE {
    215     menu_runner_.reset(NULL);
    216     menu_ = NULL;
    217     ViewEventTestBase::TearDown();
    218   }
    219 
    220   virtual views::View* CreateContentsView() OVERRIDE {
    221     return button_;
    222   }
    223 
    224   virtual gfx::Size GetPreferredSize() const OVERRIDE {
    225     return button_->GetPreferredSize();
    226   }
    227 
    228   // views::MenuButtonListener implementation.
    229   virtual void OnMenuButtonClicked(views::View* source,
    230                                    const gfx::Point& point) OVERRIDE {
    231     gfx::Point screen_location;
    232     views::View::ConvertPointToScreen(source, &screen_location);
    233     gfx::Rect bounds(screen_location, source->size());
    234     ignore_result(menu_runner_->RunMenuAt(source->GetWidget(),
    235                                           button_,
    236                                           bounds,
    237                                           views::MENU_ANCHOR_TOPLEFT,
    238                                           ui::MENU_SOURCE_NONE,
    239                                           views::MenuRunner::HAS_MNEMONICS));
    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 
    312 VIEW_TEST(MenuModelAdapterTest, RebuildMenu)
    313