Home | History | Annotate | Download | only in menu
      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 "ui/views/controls/menu/menu_model_adapter.h"
      6 
      7 #include "base/strings/utf_string_conversions.h"
      8 #include "ui/base/l10n/l10n_util.h"
      9 #include "ui/base/models/menu_model.h"
     10 #include "ui/base/models/menu_model_delegate.h"
     11 #include "ui/views/controls/menu/menu_item_view.h"
     12 #include "ui/views/controls/menu/menu_runner.h"
     13 #include "ui/views/controls/menu/submenu_view.h"
     14 #include "ui/views/test/views_test_base.h"
     15 
     16 namespace {
     17 
     18 // Base command id for test menu and its submenu.
     19 const int kRootIdBase = 100;
     20 const int kSubmenuIdBase = 200;
     21 
     22 class MenuModelBase : public ui::MenuModel {
     23  public:
     24   explicit MenuModelBase(int command_id_base)
     25       : command_id_base_(command_id_base),
     26         last_activation_(-1) {
     27   }
     28 
     29   virtual ~MenuModelBase() {
     30   }
     31 
     32   // ui::MenuModel implementation:
     33 
     34   virtual bool HasIcons() const OVERRIDE {
     35     return false;
     36   }
     37 
     38   virtual int GetItemCount() const OVERRIDE {
     39     return static_cast<int>(items_.size());
     40   }
     41 
     42   virtual ItemType GetTypeAt(int index) const OVERRIDE {
     43     return items_[index].type;
     44   }
     45 
     46   virtual ui::MenuSeparatorType GetSeparatorTypeAt(
     47       int index) const OVERRIDE {
     48     return ui::NORMAL_SEPARATOR;
     49   }
     50 
     51   virtual int GetCommandIdAt(int index) const OVERRIDE {
     52     return index + command_id_base_;
     53   }
     54 
     55   virtual base::string16 GetLabelAt(int index) const OVERRIDE {
     56     return items_[index].label;
     57   }
     58 
     59   virtual bool IsItemDynamicAt(int index) const OVERRIDE {
     60     return false;
     61   }
     62 
     63   virtual const gfx::FontList* GetLabelFontListAt(int index) const OVERRIDE {
     64     return NULL;
     65   }
     66 
     67   virtual bool GetAcceleratorAt(int index,
     68                                 ui::Accelerator* accelerator) const OVERRIDE {
     69     return false;
     70   }
     71 
     72   virtual bool IsItemCheckedAt(int index) const OVERRIDE {
     73     return false;
     74   }
     75 
     76   virtual int GetGroupIdAt(int index) const OVERRIDE {
     77     return 0;
     78   }
     79 
     80   virtual bool GetIconAt(int index, gfx::Image* icon) OVERRIDE {
     81     return false;
     82   }
     83 
     84   virtual ui::ButtonMenuItemModel* GetButtonMenuItemAt(
     85       int index) const OVERRIDE {
     86     return NULL;
     87   }
     88 
     89   virtual bool IsEnabledAt(int index) const OVERRIDE {
     90     return true;
     91   }
     92 
     93   virtual bool IsVisibleAt(int index) const OVERRIDE {
     94     return true;
     95   }
     96 
     97   virtual MenuModel* GetSubmenuModelAt(int index) const OVERRIDE {
     98     return items_[index].submenu;
     99   }
    100 
    101   virtual void HighlightChangedTo(int index) OVERRIDE {
    102   }
    103 
    104   virtual void ActivatedAt(int index) OVERRIDE {
    105     set_last_activation(index);
    106   }
    107 
    108   virtual void ActivatedAt(int index, int event_flags) OVERRIDE {
    109     ActivatedAt(index);
    110   }
    111 
    112   virtual void MenuWillShow() OVERRIDE {
    113   }
    114 
    115   virtual void MenuClosed() OVERRIDE {
    116   }
    117 
    118   virtual void SetMenuModelDelegate(
    119       ui::MenuModelDelegate* delegate) OVERRIDE {
    120   }
    121 
    122   virtual ui::MenuModelDelegate* GetMenuModelDelegate() const OVERRIDE {
    123     return NULL;
    124   }
    125 
    126   // Item definition.
    127   struct Item {
    128     Item(ItemType item_type,
    129          const std::string& item_label,
    130          ui::MenuModel* item_submenu)
    131         : type(item_type),
    132           label(base::ASCIIToUTF16(item_label)),
    133           submenu(item_submenu) {
    134     }
    135 
    136     ItemType type;
    137     base::string16 label;
    138     ui::MenuModel* submenu;
    139   };
    140 
    141   const Item& GetItemDefinition(int index) {
    142     return items_[index];
    143   }
    144 
    145   // Access index argument to ActivatedAt().
    146   int last_activation() const { return last_activation_; }
    147   void set_last_activation(int last_activation) {
    148     last_activation_ = last_activation;
    149   }
    150 
    151  protected:
    152   std::vector<Item> items_;
    153 
    154  private:
    155   int command_id_base_;
    156   int last_activation_;
    157 
    158   DISALLOW_COPY_AND_ASSIGN(MenuModelBase);
    159 };
    160 
    161 class SubmenuModel : public MenuModelBase {
    162  public:
    163   SubmenuModel() : MenuModelBase(kSubmenuIdBase) {
    164     items_.push_back(Item(TYPE_COMMAND, "submenu item 0", NULL));
    165     items_.push_back(Item(TYPE_COMMAND, "submenu item 1", NULL));
    166   }
    167 
    168   virtual ~SubmenuModel() {
    169   }
    170 
    171  private:
    172   DISALLOW_COPY_AND_ASSIGN(SubmenuModel);
    173 };
    174 
    175 class RootModel : public MenuModelBase {
    176  public:
    177   RootModel() : MenuModelBase(kRootIdBase) {
    178     submenu_model_.reset(new SubmenuModel);
    179 
    180     items_.push_back(Item(TYPE_COMMAND, "command 0", NULL));
    181     items_.push_back(Item(TYPE_CHECK, "check 1", NULL));
    182     items_.push_back(Item(TYPE_SEPARATOR, "", NULL));
    183     items_.push_back(Item(TYPE_SUBMENU, "submenu 3", submenu_model_.get()));
    184     items_.push_back(Item(TYPE_RADIO, "radio 4", NULL));
    185   }
    186 
    187   virtual ~RootModel() {
    188   }
    189 
    190  private:
    191   scoped_ptr<MenuModel> submenu_model_;
    192 
    193   DISALLOW_COPY_AND_ASSIGN(RootModel);
    194 };
    195 
    196 }  // namespace
    197 
    198 namespace views {
    199 
    200 typedef ViewsTestBase MenuModelAdapterTest;
    201 
    202 TEST_F(MenuModelAdapterTest, BasicTest) {
    203   // Build model and adapter.
    204   RootModel model;
    205   views::MenuModelAdapter delegate(&model);
    206 
    207   // Create menu.  Build menu twice to check that rebuilding works properly.
    208   MenuItemView* menu = new views::MenuItemView(&delegate);
    209   // MenuRunner takes ownership of menu.
    210   scoped_ptr<MenuRunner> menu_runner(new MenuRunner(menu));
    211   delegate.BuildMenu(menu);
    212   delegate.BuildMenu(menu);
    213   EXPECT_TRUE(menu->HasSubmenu());
    214 
    215   // Check top level menu items.
    216   views::SubmenuView* item_container = menu->GetSubmenu();
    217   EXPECT_EQ(5, item_container->child_count());
    218 
    219   for (int i = 0; i < item_container->child_count(); ++i) {
    220     const MenuModelBase::Item& model_item = model.GetItemDefinition(i);
    221 
    222     const int id = i + kRootIdBase;
    223     MenuItemView* item = menu->GetMenuItemByID(id);
    224     if (!item) {
    225       EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model_item.type);
    226       continue;
    227     }
    228 
    229     // Check placement.
    230     EXPECT_EQ(i, menu->GetSubmenu()->GetIndexOf(item));
    231 
    232     // Check type.
    233     switch (model_item.type) {
    234       case ui::MenuModel::TYPE_COMMAND:
    235         EXPECT_EQ(views::MenuItemView::NORMAL, item->GetType());
    236         break;
    237       case ui::MenuModel::TYPE_CHECK:
    238         EXPECT_EQ(views::MenuItemView::CHECKBOX, item->GetType());
    239         break;
    240       case ui::MenuModel::TYPE_RADIO:
    241         EXPECT_EQ(views::MenuItemView::RADIO, item->GetType());
    242         break;
    243       case ui::MenuModel::TYPE_SEPARATOR:
    244       case ui::MenuModel::TYPE_BUTTON_ITEM:
    245         break;
    246       case ui::MenuModel::TYPE_SUBMENU:
    247         EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
    248         break;
    249     }
    250 
    251     // Check activation.
    252     static_cast<views::MenuDelegate*>(&delegate)->ExecuteCommand(id);
    253     EXPECT_EQ(i, model.last_activation());
    254     model.set_last_activation(-1);
    255   }
    256 
    257   // Check submenu items.
    258   views::MenuItemView* submenu = menu->GetMenuItemByID(103);
    259   views::SubmenuView* subitem_container = submenu->GetSubmenu();
    260   EXPECT_EQ(2, subitem_container->child_count());
    261 
    262   for (int i = 0; i < subitem_container->child_count(); ++i) {
    263     MenuModelBase* submodel = static_cast<MenuModelBase*>(
    264         model.GetSubmenuModelAt(3));
    265     EXPECT_TRUE(submodel);
    266 
    267     const MenuModelBase::Item& model_item = submodel->GetItemDefinition(i);
    268 
    269     const int id = i + kSubmenuIdBase;
    270     MenuItemView* item = menu->GetMenuItemByID(id);
    271     if (!item) {
    272       EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, model_item.type);
    273       continue;
    274     }
    275 
    276     // Check placement.
    277     EXPECT_EQ(i, submenu->GetSubmenu()->GetIndexOf(item));
    278 
    279     // Check type.
    280     switch (model_item.type) {
    281       case ui::MenuModel::TYPE_COMMAND:
    282         EXPECT_EQ(views::MenuItemView::NORMAL, item->GetType());
    283         break;
    284       case ui::MenuModel::TYPE_CHECK:
    285         EXPECT_EQ(views::MenuItemView::CHECKBOX, item->GetType());
    286         break;
    287       case ui::MenuModel::TYPE_RADIO:
    288         EXPECT_EQ(views::MenuItemView::RADIO, item->GetType());
    289         break;
    290       case ui::MenuModel::TYPE_SEPARATOR:
    291       case ui::MenuModel::TYPE_BUTTON_ITEM:
    292         break;
    293       case ui::MenuModel::TYPE_SUBMENU:
    294         EXPECT_EQ(views::MenuItemView::SUBMENU, item->GetType());
    295         break;
    296     }
    297 
    298     // Check activation.
    299     static_cast<views::MenuDelegate*>(&delegate)->ExecuteCommand(id);
    300     EXPECT_EQ(i, submodel->last_activation());
    301     submodel->set_last_activation(-1);
    302   }
    303 
    304   // Check that selecting the root item is safe.  The MenuModel does
    305   // not care about the root so MenuModelAdapter should do nothing
    306   // (not hit the NOTREACHED check) when the root is selected.
    307   static_cast<views::MenuDelegate*>(&delegate)->SelectionChanged(menu);
    308 }
    309 
    310 }  // namespace views
    311