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