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.
5 #ifndef UI_VIEWS_CONTROLS_MENU_MENU_ITEM_VIEW_H_
6 #define UI_VIEWS_CONTROLS_MENU_MENU_ITEM_VIEW_H_
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"
24 #include "ui/native_theme/native_theme.h"
38 class TestMenuItemViewShown
;
45 // MenuItemView --------------------------------------------------------------
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.
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.
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.
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.
67 class VIEWS_EXPORT MenuItemView
: public View
{
69 friend class MenuController
;
71 // The menu item view's class name.
72 static const char kViewClassName
[];
74 // ID used to identify menu items.
75 static const int kMenuItemViewID
;
77 // ID used to identify empty menu items.
78 static const int kEmptyMenuItemViewID
;
80 // Different types of menu items. EMPTY is a special type for empty
81 // menus that is only used internally.
91 // Where the menu should be drawn, above or below the bounds (when
92 // the bounds is non-empty). POSITION_BEST_FIT (default) positions
93 // the menu below the bounds unless the menu does not fit on the
94 // screen and the re is more space above.
97 POSITION_ABOVE_BOUNDS
,
101 // The data structure which is used for the menu size
102 struct MenuItemDimensions
{
109 // Width of everything except the accelerator and children views.
111 // The width of all contained views of the item.
113 // The amount of space needed to accommodate the subtext.
114 int minor_text_width
;
115 // The height of the menu item.
119 // Constructor for use with the top level menu item. This menu is never
120 // shown to the user, rather its use as the parent for all menu items.
121 explicit MenuItemView(MenuDelegate
* delegate
);
123 // Overridden from View:
124 bool GetTooltipText(const gfx::Point
& p
,
125 base::string16
* tooltip
) const override
;
126 void GetAccessibleState(ui::AXViewState
* state
) override
;
128 // Returns the preferred height of menu items. This is only valid when the
129 // menu is about to be shown.
130 static int pref_menu_height() { return pref_menu_height_
; }
132 // X-coordinate of where the label starts.
133 static int label_start() { return label_start_
; }
135 // Returns if a given |anchor| is a bubble or not.
136 static bool IsBubble(MenuAnchorPosition anchor
);
138 // Returns the accessible name to be used with screen readers. Mnemonics are
139 // removed and the menu item accelerator text is appended.
140 static base::string16
GetAccessibleNameForMenuItem(
141 const base::string16
& item_text
, const base::string16
& accelerator_text
);
143 // Hides and cancels the menu. This does nothing if the menu is not open.
146 // Add an item to the menu at a specified index. ChildrenChanged() should
147 // called after adding menu items if the menu may be active.
148 MenuItemView
* AddMenuItemAt(int index
,
150 const base::string16
& label
,
151 const base::string16
& sublabel
,
152 const base::string16
& minor_text
,
153 const gfx::ImageSkia
& icon
,
155 ui::MenuSeparatorType separator_style
);
157 // Remove an item from the menu at a specified index. The removed MenuItemView
158 // is deleted when ChildrenChanged() is invoked.
159 void RemoveMenuItemAt(int index
);
161 // Appends an item to this menu.
162 // item_id The id of the item, used to identify it in delegate callbacks
163 // or (if delegate is NULL) to identify the command associated
164 // with this item with the controller specified in the ctor. Note
165 // that this value should not be 0 as this has a special meaning
166 // ("NULL command, no item selected")
167 // label The text label shown.
168 // type The type of item.
169 MenuItemView
* AppendMenuItem(int item_id
,
170 const base::string16
& label
,
173 // Append a submenu to this menu.
174 // The returned pointer is owned by this menu.
175 MenuItemView
* AppendSubMenu(int item_id
,
176 const base::string16
& label
);
178 // Append a submenu with an icon to this menu.
179 // The returned pointer is owned by this menu.
180 MenuItemView
* AppendSubMenuWithIcon(int item_id
,
181 const base::string16
& label
,
182 const gfx::ImageSkia
& icon
);
184 // This is a convenience for standard text label menu items where the label
185 // is provided with this call.
186 MenuItemView
* AppendMenuItemWithLabel(int item_id
,
187 const base::string16
& label
);
189 // This is a convenience for text label menu items where the label is
190 // provided by the delegate.
191 MenuItemView
* AppendDelegateMenuItem(int item_id
);
193 // Adds a separator to this menu
194 void AppendSeparator();
196 // Appends a menu item with an icon. This is for the menu item which
197 // needs an icon. Calling this function forces the Menu class to draw
198 // the menu, instead of relying on Windows.
199 MenuItemView
* AppendMenuItemWithIcon(int item_id
,
200 const base::string16
& label
,
201 const gfx::ImageSkia
& icon
);
203 // All the AppendXXX methods funnel into this.
204 MenuItemView
* AppendMenuItemImpl(int item_id
,
205 const base::string16
& label
,
206 const base::string16
& sublabel
,
207 const base::string16
& minor_text
,
208 const gfx::ImageSkia
& icon
,
210 ui::MenuSeparatorType separator_style
);
212 // Returns the view that contains child menu items. If the submenu has
213 // not been creates, this creates it.
214 virtual SubmenuView
* CreateSubmenu();
216 // Returns true if this menu item has a submenu.
217 virtual bool HasSubmenu() const;
219 // Returns the view containing child menu items.
220 virtual SubmenuView
* GetSubmenu() const;
222 // Returns the parent menu item.
223 MenuItemView
* GetParentMenuItem() { return parent_menu_item_
; }
224 const MenuItemView
* GetParentMenuItem() const { return parent_menu_item_
; }
226 // Sets/Gets the title.
227 void SetTitle(const base::string16
& title
);
228 const base::string16
& title() const { return title_
; }
230 // Sets the subtitle.
231 void SetSubtitle(const base::string16
& subtitle
);
233 // Sets the minor text.
234 void SetMinorText(const base::string16
& minor_text
);
236 // Returns the type of this menu.
237 const Type
& GetType() const { return type_
; }
239 // Sets whether this item is selected. This is invoked as the user moves
240 // the mouse around the menu while open.
241 void SetSelected(bool selected
);
243 // Returns true if the item is selected.
244 bool IsSelected() const { return selected_
; }
246 // Sets the |tooltip| for a menu item view with |item_id| identifier.
247 void SetTooltip(const base::string16
& tooltip
, int item_id
);
249 // Sets the icon for the descendant identified by item_id.
250 void SetIcon(const gfx::ImageSkia
& icon
, int item_id
);
252 // Sets the icon of this menu item.
253 void SetIcon(const gfx::ImageSkia
& icon
);
255 // Sets the view used to render the icon. This clobbers any icon set via
256 // SetIcon(). MenuItemView takes ownership of |icon_view|.
257 void SetIconView(View
* icon_view
);
258 View
* icon_view() { return icon_view_
; }
260 // Sets the command id of this menu item.
261 void SetCommand(int command
) { command_
= command
; }
263 // Returns the command id of this item.
264 int GetCommand() const { return command_
; }
266 // Paints the menu item.
267 void OnPaint(gfx::Canvas
* canvas
) override
;
269 // Returns the preferred size of this item.
270 gfx::Size
GetPreferredSize() const override
;
272 // Gets the preferred height for the given |width|. This is only different
273 // from GetPreferredSize().width() if the item has a child view with flexible
275 int GetHeightForWidth(int width
) const override
;
277 // Return the preferred dimensions of the item in pixel.
278 const MenuItemDimensions
& GetDimensions() const;
280 // Returns the object responsible for controlling showing the menu.
281 MenuController
* GetMenuController();
282 const MenuController
* GetMenuController() const;
284 // Returns the delegate. This returns the delegate of the root menu item.
285 MenuDelegate
* GetDelegate();
286 const MenuDelegate
* GetDelegate() const;
287 void set_delegate(MenuDelegate
* delegate
) { delegate_
= delegate
; }
289 // Returns the root parent, or this if this has no parent.
290 MenuItemView
* GetRootMenuItem();
291 const MenuItemView
* GetRootMenuItem() const;
293 // Returns the mnemonic for this MenuItemView, or 0 if this MenuItemView
294 // doesn't have a mnemonic.
295 base::char16
GetMnemonic();
297 // Do we have icons? This only has effect on the top menu. Turning this on
298 // makes the menus slightly wider and taller.
299 void set_has_icons(bool has_icons
) {
300 has_icons_
= has_icons
;
302 bool has_icons() const { return has_icons_
; }
304 // Returns the descendant with the specified command.
305 MenuItemView
* GetMenuItemByID(int id
);
307 // Invoke if you remove/add children to the menu while it's showing. This
308 // recalculates the bounds.
309 void ChildrenChanged();
311 // Sizes any child views.
312 void Layout() override
;
314 // Returns true if the menu has mnemonics. This only useful on the root menu
316 bool has_mnemonics() const { return has_mnemonics_
; }
318 // Set top and bottom margins in pixels. If no margin is set or a
319 // negative margin is specified then MenuConfig values are used.
320 void SetMargins(int top_margin
, int bottom_margin
);
322 // Suppress the right margin if this is set to false.
323 void set_use_right_margin(bool use_right_margin
) {
324 use_right_margin_
= use_right_margin
;
327 // Returns a reference to MenuConfig to be used with this menu.
328 const MenuConfig
& GetMenuConfig() const;
331 // Creates a MenuItemView. This is used by the various AddXXX methods.
332 MenuItemView(MenuItemView
* parent
, int command
, Type type
);
334 // MenuRunner owns MenuItemView and should be the only one deleting it.
335 ~MenuItemView() override
;
337 void ChildPreferredSizeChanged(View
* child
) override
;
339 const char* GetClassName() const override
;
341 // Returns the preferred size (and padding) of any children.
342 virtual gfx::Size
GetChildPreferredSize() const;
344 // Returns the various margins.
345 int GetTopMargin() const;
346 int GetBottomMargin() const;
349 friend class internal::MenuRunnerImpl
; // For access to ~MenuItemView.
350 friend class test::TestMenuItemViewShown
; // for access to |submenu_|;
352 enum PaintButtonMode
{ PB_NORMAL
, PB_FOR_DRAG
};
354 // Calculates all sizes that we can from the OS.
356 // This is invoked prior to Running a menu.
357 void UpdateMenuPartSizes();
359 // Called by the two constructors to initialize this menu item.
360 void Init(MenuItemView
* parent
,
362 MenuItemView::Type type
,
363 MenuDelegate
* delegate
);
365 // The RunXXX methods call into this to set up the necessary state before
366 // running. |is_first_menu| is true if no menus are currently showing.
367 void PrepareForRun(bool is_first_menu
,
369 bool show_mnemonics
);
371 // Returns the flags passed to DrawStringRect.
372 int GetDrawStringFlags();
374 // Returns the font list to use for menu text.
375 const gfx::FontList
& GetFontList() const;
377 // If this menu item has no children a child is added showing it has no
378 // children. Otherwise AddEmtpyMenus is recursively invoked on child menu
379 // items that have children.
380 void AddEmptyMenus();
382 // Undoes the work of AddEmptyMenus.
383 void RemoveEmptyMenus();
385 // Given bounds within our View, this helper routine mirrors the bounds if
387 void AdjustBoundsForRTLUI(gfx::Rect
* rect
) const;
389 // Actual paint implementation. If mode is PB_FOR_DRAG, portions of the menu
391 void PaintButton(gfx::Canvas
* canvas
, PaintButtonMode mode
);
393 // Paints the right-side text.
394 void PaintMinorText(gfx::Canvas
* canvas
, bool render_selection
);
396 // Destroys the window used to display this menu and recursively destroys
397 // the windows used to display all descendants.
398 void DestroyAllMenuHosts();
400 // Returns the text that should be displayed on the end (right) of the menu
401 // item. This will be the accelerator (if one exists), otherwise |subtitle_|.
402 base::string16
GetMinorText() const;
404 // Calculates and returns the MenuItemDimensions.
405 MenuItemDimensions
CalculateDimensions() const;
407 // Get the horizontal position at which to draw the menu item's label.
408 int GetLabelStartForThisItem() const;
410 // Used by MenuController to cache the menu position in use by the
412 MenuPosition
actual_menu_position() const { return actual_menu_position_
; }
413 void set_actual_menu_position(MenuPosition actual_menu_position
) {
414 actual_menu_position_
= actual_menu_position
;
417 void set_controller(MenuController
* controller
) { controller_
= controller
; }
419 // Returns true if this MenuItemView contains a single child
420 // that is responsible for rendering the content.
421 bool IsContainer() const;
423 // Returns number of child views excluding icon_view.
424 int NonIconChildViewsCount() const;
426 // Returns the max icon width; recurses over submenus.
427 int GetMaxIconViewWidth() const;
429 // Returns true if the menu has items with a checkbox or a radio button.
430 bool HasChecksOrRadioButtons() const;
432 void invalidate_dimensions() { dimensions_
.height
= 0; }
433 bool is_dimensions_valid() const { return dimensions_
.height
> 0; }
435 // The delegate. This is only valid for the root menu item. You shouldn't
436 // use this directly, instead use GetDelegate() which walks the tree as
438 MenuDelegate
* delegate_
;
440 // The controller for the run operation, or NULL if the menu isn't showing.
441 MenuController
* controller_
;
443 // Used to detect when Cancel was invoked.
447 MenuItemView
* parent_menu_item_
;
449 // Type of menu. NOTE: MenuItemView doesn't itself represent SEPARATOR,
450 // that is handled by an entirely different view class.
453 // Whether we're selected.
459 // Submenu, created via CreateSubmenu.
460 SubmenuView
* submenu_
;
463 base::string16 title_
;
465 // Subtitle/sublabel.
466 base::string16 subtitle_
;
469 base::string16 minor_text_
;
471 // Does the title have a mnemonic? Only useful on the root menu item.
474 // Should we show the mnemonic? Mnemonics are shown if this is true or
475 // MenuConfig says mnemonics should be shown. Only used on the root menu item.
476 bool show_mnemonics_
;
478 // Set if menu has icons or icon_views (applies to root menu item only).
481 // Pointer to a view with a menu icon.
484 // The tooltip to show on hover for this menu item.
485 base::string16 tooltip_
;
487 // Width of a menu icon area.
488 static int icon_area_width_
;
490 // X-coordinate of where the label starts.
491 static int label_start_
;
493 // Margins between the right of the item and the label.
494 static int item_right_margin_
;
496 // Preferred height of menu items. Reset every time a menu is run.
497 static int pref_menu_height_
;
499 // Cached dimensions. This is cached as text sizing calculations are quite
501 mutable MenuItemDimensions dimensions_
;
503 // Removed items to be deleted in ChildrenChanged().
504 std::vector
<View
*> removed_items_
;
506 // Margins in pixels.
510 // Horizontal icon margins in pixels, which can differ between MenuItems.
511 // These values will be set in the layout process.
512 mutable int left_icon_margin_
;
513 mutable int right_icon_margin_
;
515 // |menu_position_| is the requested position with respect to the bounds.
516 // |actual_menu_position_| is used by the controller to cache the
517 // position of the menu being shown.
518 MenuPosition requested_menu_position_
;
519 MenuPosition actual_menu_position_
;
521 // If set to false, the right margin will be removed for menu lines
522 // containing other elements.
523 bool use_right_margin_
;
525 DISALLOW_COPY_AND_ASSIGN(MenuItemView
);
530 #endif // UI_VIEWS_CONTROLS_MENU_MENU_ITEM_VIEW_H_