• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
MenuItemDimensionsMenuItemDimensions99     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.
pref_menu_height()126   static int pref_menu_height() { return pref_menu_height_; }
127 
128   // X-coordinate of where the label starts.
label_start()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.
GetParentMenuItem()219   MenuItemView* GetParentMenuItem() { return parent_menu_item_; }
GetParentMenuItem()220   const MenuItemView* GetParentMenuItem() const { return parent_menu_item_; }
221 
222   // Sets/Gets the title.
223   void SetTitle(const base::string16& title);
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.
GetType()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.
IsSelected()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);
icon_view()254   View* icon_view() { return icon_view_; }
255 
256   // Sets the command id of this menu item.
SetCommand(int command)257   void SetCommand(int command) { command_ = command; }
258 
259   // Returns the command id of this item.
GetCommand()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;
set_delegate(MenuDelegate * delegate)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.
set_has_icons(bool has_icons)295   void set_has_icons(bool has_icons) {
296     has_icons_ = has_icons;
297   }
has_icons()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.
has_mnemonics()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.
set_use_right_margin(bool use_right_margin)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.
actual_menu_position()407   MenuPosition actual_menu_position() const { return actual_menu_position_; }
set_actual_menu_position(MenuPosition actual_menu_position)408   void set_actual_menu_position(MenuPosition actual_menu_position) {
409     actual_menu_position_ = actual_menu_position;
410   }
411 
set_controller(MenuController * controller)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 
invalidate_dimensions()427   void invalidate_dimensions() { dimensions_.height = 0; }
is_dimensions_valid()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