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
27namespace gfx {
28class FontList;
29}
30
31namespace views {
32
33namespace internal {
34class MenuRunnerImpl;
35}
36
37class MenuController;
38class MenuDelegate;
39class 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
63class 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