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