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 #ifndef UI_VIEWS_CONTROLS_MENU_MENU_ITEM_VIEW_H_
      6 #define UI_VIEWS_CONTROLS_MENU_MENU_ITEM_VIEW_H_
      7 
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/compiler_specific.h"
     12 #include "base/logging.h"
     13 #include "base/strings/string16.h"
     14 #include "build/build_config.h"
     15 #include "ui/base/models/menu_separator_types.h"
     16 #include "ui/gfx/image/image_skia.h"
     17 #include "ui/views/controls/menu/menu_config.h"
     18 #include "ui/views/view.h"
     19 
     20 #if defined(OS_WIN)
     21 #include <windows.h>
     22 
     23 #include "ui/native_theme/native_theme.h"
     24 #endif
     25 
     26 namespace gfx {
     27 class Font;
     28 }
     29 
     30 namespace ui {
     31 class MenuModel;
     32 class NativeTheme;
     33 }
     34 
     35 namespace views {
     36 
     37 namespace internal {
     38 class MenuRunnerImpl;
     39 }
     40 
     41 class MenuController;
     42 class MenuDelegate;
     43 class SubmenuView;
     44 
     45 // MenuItemView --------------------------------------------------------------
     46 
     47 // MenuItemView represents a single menu item with a label and optional icon.
     48 // Each MenuItemView may also contain a submenu, which in turn may contain
     49 // any number of child MenuItemViews.
     50 //
     51 // To use a menu create an initial MenuItemView using the constructor that
     52 // takes a MenuDelegate, then create any number of child menu items by way
     53 // of the various AddXXX methods.
     54 //
     55 // MenuItemView is itself a View, which means you can add Views to each
     56 // MenuItemView. This is normally NOT want you want, rather add other child
     57 // Views to the submenu of the MenuItemView. Any child views of the MenuItemView
     58 // that are focusable can be navigated to by way of the up/down arrow and can be
     59 // activated by way of space/return keys. Activating a focusable child results
     60 // in |AcceleratorPressed| being invoked. Note, that as menus try not to steal
     61 // focus from the hosting window child views do not actually get focus. Instead
     62 // |SetHotTracked| is used as the user navigates around.
     63 //
     64 // To show the menu use MenuRunner. See MenuRunner for details on how to run
     65 // (show) the menu as well as for details on the life time of the menu.
     66 
     67 class VIEWS_EXPORT MenuItemView : public View {
     68  public:
     69   friend class MenuController;
     70 
     71   // The menu item view's class name.
     72   static const char kViewClassName[];
     73 
     74   // ID used to identify menu items.
     75   static const int kMenuItemViewID;
     76 
     77   // ID used to identify empty menu items.
     78   static const int kEmptyMenuItemViewID;
     79 
     80   // Different types of menu items.  EMPTY is a special type for empty
     81   // menus that is only used internally.
     82   enum Type {
     83     NORMAL,
     84     SUBMENU,
     85     CHECKBOX,
     86     RADIO,
     87     SEPARATOR,
     88     EMPTY
     89   };
     90 
     91   // Where the menu should be anchored to for non-RTL languages.  The
     92   // opposite position will be used if base::i18n:IsRTL() is true.
     93   // The BUBBLE flags are used when the menu should get enclosed by a bubble.
     94   // Note that BUBBLE flags should only be used with menus which have no
     95   // children.
     96   enum AnchorPosition {
     97     TOPLEFT,
     98     TOPRIGHT,
     99     BOTTOMCENTER,
    100     BUBBLE_LEFT,
    101     BUBBLE_RIGHT,
    102     BUBBLE_ABOVE,
    103     BUBBLE_BELOW
    104   };
    105 
    106   // Where the menu should be drawn, above or below the bounds (when
    107   // the bounds is non-empty).  POSITION_BEST_FIT (default) positions
    108   // the menu below the bounds unless the menu does not fit on the
    109   // screen and the re is more space above.
    110   enum MenuPosition {
    111     POSITION_BEST_FIT,
    112     POSITION_ABOVE_BOUNDS,
    113     POSITION_BELOW_BOUNDS
    114   };
    115 
    116   // The data structure which is used for the menu size
    117   struct MenuItemDimensions {
    118     MenuItemDimensions()
    119         : standard_width(0),
    120           children_width(0),
    121           minor_text_width(0),
    122           height(0) {}
    123 
    124     // Width of everything except the accelerator and children views.
    125     int standard_width;
    126     // The width of all contained views of the item.
    127     int children_width;
    128     // The amount of space needed to accommodate the subtext.
    129     int minor_text_width;
    130     // The height of the menu item.
    131     int height;
    132   };
    133 
    134   // Constructor for use with the top level menu item. This menu is never
    135   // shown to the user, rather its use as the parent for all menu items.
    136   explicit MenuItemView(MenuDelegate* delegate);
    137 
    138   // Overridden from View:
    139   virtual bool GetTooltipText(const gfx::Point& p,
    140                               string16* tooltip) const OVERRIDE;
    141   virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
    142 
    143   // Returns the preferred height of menu items. This is only valid when the
    144   // menu is about to be shown.
    145   static int pref_menu_height() { return pref_menu_height_; }
    146 
    147   // X-coordinate of where the label starts.
    148   static int label_start() { return label_start_; }
    149 
    150   // Returns if a given |anchor| is a bubble or not.
    151   static bool IsBubble(MenuItemView::AnchorPosition anchor);
    152 
    153   // Returns the accessible name to be used with screen readers. Mnemonics are
    154   // removed and the menu item accelerator text is appended.
    155   static string16 GetAccessibleNameForMenuItem(
    156       const string16& item_text, const string16& accelerator_text);
    157 
    158   // Hides and cancels the menu. This does nothing if the menu is not open.
    159   void Cancel();
    160 
    161   // Add an item to the menu at a specified index.  ChildrenChanged() should
    162   // called after adding menu items if the menu may be active.
    163   MenuItemView* AddMenuItemAt(int index,
    164                               int item_id,
    165                               const string16& label,
    166                               const string16& sublabel,
    167                               const gfx::ImageSkia& icon,
    168                               Type type,
    169                               ui::MenuSeparatorType separator_style);
    170 
    171   // Remove an item from the menu at a specified index. The removed MenuItemView
    172   // is deleted when ChildrenChanged() is invoked.
    173   void RemoveMenuItemAt(int index);
    174 
    175   // Appends an item to this menu.
    176   // item_id    The id of the item, used to identify it in delegate callbacks
    177   //            or (if delegate is NULL) to identify the command associated
    178   //            with this item with the controller specified in the ctor. Note
    179   //            that this value should not be 0 as this has a special meaning
    180   //            ("NULL command, no item selected")
    181   // label      The text label shown.
    182   // type       The type of item.
    183   MenuItemView* AppendMenuItem(int item_id,
    184                                const string16& label,
    185                                Type type);
    186 
    187   // Append a submenu to this menu.
    188   // The returned pointer is owned by this menu.
    189   MenuItemView* AppendSubMenu(int item_id,
    190                               const string16& label);
    191 
    192   // Append a submenu with an icon to this menu.
    193   // The returned pointer is owned by this menu.
    194   MenuItemView* AppendSubMenuWithIcon(int item_id,
    195                                       const string16& label,
    196                                       const gfx::ImageSkia& icon);
    197 
    198   // This is a convenience for standard text label menu items where the label
    199   // is provided with this call.
    200   MenuItemView* AppendMenuItemWithLabel(int item_id,
    201                                         const string16& label);
    202 
    203   // This is a convenience for text label menu items where the label is
    204   // provided by the delegate.
    205   MenuItemView* AppendDelegateMenuItem(int item_id);
    206 
    207   // Adds a separator to this menu
    208   void AppendSeparator();
    209 
    210   // Appends a menu item with an icon. This is for the menu item which
    211   // needs an icon. Calling this function forces the Menu class to draw
    212   // the menu, instead of relying on Windows.
    213   MenuItemView* AppendMenuItemWithIcon(int item_id,
    214                                        const string16& label,
    215                                        const gfx::ImageSkia& icon);
    216 
    217   // Creates a menu item for the specified entry in the model and appends it as
    218   // a child.
    219   MenuItemView* AppendMenuItemFromModel(ui::MenuModel* model,
    220                                         int index,
    221                                         int id);
    222 
    223   // All the AppendXXX methods funnel into this.
    224   MenuItemView* AppendMenuItemImpl(int item_id,
    225                                    const string16& label,
    226                                    const string16& sublabel,
    227                                    const gfx::ImageSkia& icon,
    228                                    Type type,
    229                                    ui::MenuSeparatorType separator_style);
    230 
    231   // Returns the view that contains child menu items. If the submenu has
    232   // not been creates, this creates it.
    233   virtual SubmenuView* CreateSubmenu();
    234 
    235   // Returns true if this menu item has a submenu.
    236   virtual bool HasSubmenu() const;
    237 
    238   // Returns the view containing child menu items.
    239   virtual SubmenuView* GetSubmenu() const;
    240 
    241   // Returns the parent menu item.
    242   MenuItemView* GetParentMenuItem() { return parent_menu_item_; }
    243   const MenuItemView* GetParentMenuItem() const { return parent_menu_item_; }
    244 
    245   // Sets/Gets the title.
    246   void SetTitle(const string16& title);
    247   const string16& title() const { return title_; }
    248 
    249   // Sets the subtitle.
    250   void SetSubtitle(const string16& subtitle);
    251 
    252   // Returns the type of this menu.
    253   const Type& GetType() const { return type_; }
    254 
    255   // Sets whether this item is selected. This is invoked as the user moves
    256   // the mouse around the menu while open.
    257   void SetSelected(bool selected);
    258 
    259   // Returns true if the item is selected.
    260   bool IsSelected() const { return selected_; }
    261 
    262   // Sets the |tooltip| for a menu item view with |item_id| identifier.
    263   void SetTooltip(const string16& tooltip, int item_id);
    264 
    265   // Sets the icon for the descendant identified by item_id.
    266   void SetIcon(const gfx::ImageSkia& icon, int item_id);
    267 
    268   // Sets the icon of this menu item.
    269   void SetIcon(const gfx::ImageSkia& icon);
    270 
    271   // Sets the view used to render the icon. This clobbers any icon set via
    272   // SetIcon(). MenuItemView takes ownership of |icon_view|.
    273   void SetIconView(View* icon_view);
    274   View* icon_view() { return icon_view_; }
    275 
    276   // Sets the command id of this menu item.
    277   void SetCommand(int command) { command_ = command; }
    278 
    279   // Returns the command id of this item.
    280   int GetCommand() const { return command_; }
    281 
    282   // Paints the menu item.
    283   virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE;
    284 
    285   // Returns the preferred size of this item.
    286   virtual gfx::Size GetPreferredSize() OVERRIDE;
    287 
    288   // Return the preferred dimensions of the item in pixel.
    289   const MenuItemDimensions& GetDimensions();
    290 
    291   // Returns the object responsible for controlling showing the menu.
    292   MenuController* GetMenuController();
    293   const MenuController* GetMenuController() const;
    294 
    295   // Returns the delegate. This returns the delegate of the root menu item.
    296   MenuDelegate* GetDelegate();
    297   const MenuDelegate* GetDelegate() const;
    298   void set_delegate(MenuDelegate* delegate) { delegate_ = delegate; }
    299 
    300   // Returns the root parent, or this if this has no parent.
    301   MenuItemView* GetRootMenuItem();
    302   const MenuItemView* GetRootMenuItem() const;
    303 
    304   // Returns the mnemonic for this MenuItemView, or 0 if this MenuItemView
    305   // doesn't have a mnemonic.
    306   char16 GetMnemonic();
    307 
    308   // Do we have icons? This only has effect on the top menu. Turning this on
    309   // makes the menus slightly wider and taller.
    310   void set_has_icons(bool has_icons) {
    311     has_icons_ = has_icons;
    312   }
    313   bool has_icons() const { return has_icons_; }
    314 
    315   // Returns the descendant with the specified command.
    316   MenuItemView* GetMenuItemByID(int id);
    317 
    318   // Invoke if you remove/add children to the menu while it's showing. This
    319   // recalculates the bounds.
    320   void ChildrenChanged();
    321 
    322   // Sizes any child views.
    323   virtual void Layout() OVERRIDE;
    324 
    325   // Returns true if the menu has mnemonics. This only useful on the root menu
    326   // item.
    327   bool has_mnemonics() const { return has_mnemonics_; }
    328 
    329   // Set top and bottom margins in pixels.  If no margin is set or a
    330   // negative margin is specified then MenuConfig values are used.
    331   void SetMargins(int top_margin, int bottom_margin);
    332 
    333   // Suppress the right margin if this is set to false.
    334   void set_use_right_margin(bool use_right_margin) {
    335     use_right_margin_ = use_right_margin;
    336   }
    337 
    338   // Returns a reference to MenuConfig to be used with this menu.
    339   const MenuConfig& GetMenuConfig() const;
    340 
    341  protected:
    342   // Creates a MenuItemView. This is used by the various AddXXX methods.
    343   MenuItemView(MenuItemView* parent, int command, Type type);
    344 
    345   // MenuRunner owns MenuItemView and should be the only one deleting it.
    346   virtual ~MenuItemView();
    347 
    348   virtual void ChildPreferredSizeChanged(View* child) OVERRIDE;
    349 
    350   virtual const char* GetClassName() const OVERRIDE;
    351 
    352   // Returns the preferred size (and padding) of any children.
    353   virtual gfx::Size GetChildPreferredSize();
    354 
    355   // Returns the various margins.
    356   int GetTopMargin();
    357   int GetBottomMargin();
    358 
    359  private:
    360   friend class internal::MenuRunnerImpl;  // For access to ~MenuItemView.
    361 
    362   enum PaintButtonMode { PB_NORMAL, PB_FOR_DRAG };
    363 
    364   // Calculates all sizes that we can from the OS.
    365   //
    366   // This is invoked prior to Running a menu.
    367   void UpdateMenuPartSizes();
    368 
    369   // Called by the two constructors to initialize this menu item.
    370   void Init(MenuItemView* parent,
    371             int command,
    372             MenuItemView::Type type,
    373             MenuDelegate* delegate);
    374 
    375   // The RunXXX methods call into this to set up the necessary state before
    376   // running. |is_first_menu| is true if no menus are currently showing.
    377   void PrepareForRun(bool is_first_menu,
    378                      bool has_mnemonics,
    379                      bool show_mnemonics);
    380 
    381   // Returns the flags passed to DrawStringInt.
    382   int GetDrawStringFlags();
    383 
    384   // Returns the font to use for menu text.
    385   const gfx::Font& GetFont();
    386 
    387   // If this menu item has no children a child is added showing it has no
    388   // children. Otherwise AddEmtpyMenus is recursively invoked on child menu
    389   // items that have children.
    390   void AddEmptyMenus();
    391 
    392   // Undoes the work of AddEmptyMenus.
    393   void RemoveEmptyMenus();
    394 
    395   // Given bounds within our View, this helper routine mirrors the bounds if
    396   // necessary.
    397   void AdjustBoundsForRTLUI(gfx::Rect* rect) const;
    398 
    399   // Actual paint implementation. If mode is PB_FOR_DRAG, portions of the menu
    400   // are not rendered.
    401   void PaintButton(gfx::Canvas* canvas, PaintButtonMode mode);
    402 
    403   // Paints the right-side text.
    404   void PaintMinorText(gfx::Canvas* canvas, bool render_selection);
    405 
    406   // Destroys the window used to display this menu and recursively destroys
    407   // the windows used to display all descendants.
    408   void DestroyAllMenuHosts();
    409 
    410   // Returns the text that should be displayed on the end (right) of the menu
    411   // item. This will be the accelerator (if one exists), otherwise |subtitle_|.
    412   string16 GetMinorText();
    413 
    414   // Calculates and returns the MenuItemDimensions.
    415   MenuItemDimensions CalculateDimensions();
    416 
    417   // Get the horizontal position at which to draw the menu item's label.
    418   int GetLabelStartForThisItem();
    419 
    420   // Used by MenuController to cache the menu position in use by the
    421   // active menu.
    422   MenuPosition actual_menu_position() const { return actual_menu_position_; }
    423   void set_actual_menu_position(MenuPosition actual_menu_position) {
    424     actual_menu_position_ = actual_menu_position;
    425   }
    426 
    427   void set_controller(MenuController* controller) { controller_ = controller; }
    428 
    429   // Returns true if this MenuItemView contains a single child
    430   // that is responsible for rendering the content.
    431   bool IsContainer() const;
    432 
    433   // Returns number of child views excluding icon_view.
    434   int NonIconChildViewsCount() const;
    435 
    436   // Returns the max icon width; recurses over submenus.
    437   int GetMaxIconViewWidth() const;
    438 
    439   // Returns true if the menu has items with a checkbox or a radio button.
    440   bool HasChecksOrRadioButtons() const;
    441 
    442   void invalidate_dimensions() { dimensions_.height = 0; }
    443   bool is_dimensions_valid() const { return dimensions_.height > 0; }
    444 
    445   // The delegate. This is only valid for the root menu item. You shouldn't
    446   // use this directly, instead use GetDelegate() which walks the tree as
    447   // as necessary.
    448   MenuDelegate* delegate_;
    449 
    450   // The controller for the run operation, or NULL if the menu isn't showing.
    451   MenuController* controller_;
    452 
    453   // Used to detect when Cancel was invoked.
    454   bool canceled_;
    455 
    456   // Our parent.
    457   MenuItemView* parent_menu_item_;
    458 
    459   // Type of menu. NOTE: MenuItemView doesn't itself represent SEPARATOR,
    460   // that is handled by an entirely different view class.
    461   Type type_;
    462 
    463   // Whether we're selected.
    464   bool selected_;
    465 
    466   // Command id.
    467   int command_;
    468 
    469   // Submenu, created via CreateSubmenu.
    470   SubmenuView* submenu_;
    471 
    472   // Title.
    473   string16 title_;
    474 
    475   // Subtitle/sublabel.
    476   string16 subtitle_;
    477 
    478   // Does the title have a mnemonic? Only useful on the root menu item.
    479   bool has_mnemonics_;
    480 
    481   // Should we show the mnemonic? Mnemonics are shown if this is true or
    482   // MenuConfig says mnemonics should be shown. Only used on the root menu item.
    483   bool show_mnemonics_;
    484 
    485   // Set if menu has icons or icon_views (applies to root menu item only).
    486   bool has_icons_;
    487 
    488   // Pointer to a view with a menu icon.
    489   View* icon_view_;
    490 
    491   // The tooltip to show on hover for this menu item.
    492   string16 tooltip_;
    493 
    494   // Width of a menu icon area.
    495   static int icon_area_width_;
    496 
    497   // X-coordinate of where the label starts.
    498   static int label_start_;
    499 
    500   // Margins between the right of the item and the label.
    501   static int item_right_margin_;
    502 
    503   // Preferred height of menu items. Reset every time a menu is run.
    504   static int pref_menu_height_;
    505 
    506   // Cached dimensions. This is cached as text sizing calculations are quite
    507   // costly.
    508   MenuItemDimensions dimensions_;
    509 
    510   // Removed items to be deleted in ChildrenChanged().
    511   std::vector<View*> removed_items_;
    512 
    513   // Margins in pixels.
    514   int top_margin_;
    515   int bottom_margin_;
    516 
    517   // Horizontal icon margins in pixels, which can differ between MenuItems.
    518   // These values will be set in the layout process.
    519   int left_icon_margin_;
    520   int right_icon_margin_;
    521 
    522   // |menu_position_| is the requested position with respect to the bounds.
    523   // |actual_menu_position_| is used by the controller to cache the
    524   // position of the menu being shown.
    525   MenuPosition requested_menu_position_;
    526   MenuPosition actual_menu_position_;
    527 
    528   // If set to false, the right margin will be removed for menu lines
    529   // containing other elements.
    530   bool use_right_margin_;
    531 
    532   DISALLOW_COPY_AND_ASSIGN(MenuItemView);
    533 };
    534 
    535 }  // namespace views
    536 
    537 #endif  // UI_VIEWS_CONTROLS_MENU_MENU_ITEM_VIEW_H_
    538