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