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