| OLD | NEW |
| (Empty) |
| 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 { | |
| 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_ | |
| OLD | NEW |