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