1 /* 2 ============================================================================== 3 4 This file is part of the JUCE library. 5 Copyright (c) 2020 - Raw Material Software Limited 6 7 JUCE is an open source library subject to commercial or open-source 8 licensing. 9 10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License 11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020). 12 13 End User License Agreement: www.juce.com/juce-6-licence 14 Privacy Policy: www.juce.com/juce-privacy-policy 15 16 Or: You may also use this code under the terms of the GPL v3 (see 17 www.gnu.org/licenses). 18 19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER 20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE 21 DISCLAIMED. 22 23 ============================================================================== 24 */ 25 26 namespace juce 27 { 28 29 //============================================================================== 30 /** Creates and displays a popup-menu. 31 32 To show a popup-menu, you create one of these, add some items to it, then 33 call its show() method, which returns the id of the item the user selects. 34 35 E.g. @code 36 void MyWidget::mouseDown (const MouseEvent& e) 37 { 38 PopupMenu m; 39 m.addItem (1, "item 1"); 40 m.addItem (2, "item 2"); 41 42 const int result = m.show(); 43 44 if (result == 0) 45 { 46 // user dismissed the menu without picking anything 47 } 48 else if (result == 1) 49 { 50 // user picked item 1 51 } 52 else if (result == 2) 53 { 54 // user picked item 2 55 } 56 } 57 @endcode 58 59 Submenus are easy too: @code 60 61 void MyWidget::mouseDown (const MouseEvent& e) 62 { 63 PopupMenu subMenu; 64 subMenu.addItem (1, "item 1"); 65 subMenu.addItem (2, "item 2"); 66 67 PopupMenu mainMenu; 68 mainMenu.addItem (3, "item 3"); 69 mainMenu.addSubMenu ("other choices", subMenu); 70 71 const int result = m.show(); 72 73 ...etc 74 } 75 @endcode 76 77 @tags{GUI} 78 */ 79 class JUCE_API PopupMenu 80 { 81 public: 82 //============================================================================== 83 /** Creates an empty popup menu. */ 84 PopupMenu() = default; 85 86 /** Creates a copy of another menu. */ 87 PopupMenu (const PopupMenu&); 88 89 /** Destructor. */ 90 ~PopupMenu(); 91 92 /** Copies this menu from another one. */ 93 PopupMenu& operator= (const PopupMenu&); 94 95 /** Move constructor */ 96 PopupMenu (PopupMenu&&) noexcept; 97 98 /** Move assignment operator */ 99 PopupMenu& operator= (PopupMenu&&) noexcept; 100 101 //============================================================================== 102 class CustomComponent; 103 class CustomCallback; 104 105 //============================================================================== 106 /** Resets the menu, removing all its items. */ 107 void clear(); 108 109 /** Describes a popup menu item. */ 110 struct JUCE_API Item 111 { 112 /** Creates a null item. 113 You'll need to set some fields after creating an Item before you 114 can add it to a PopupMenu 115 */ 116 Item(); 117 118 /** Creates an item with the given text. 119 This constructor also initialises the itemID to -1, which makes it suitable for 120 creating lambda-based item actions. 121 */ 122 Item (String text); 123 124 Item (const Item&); 125 Item& operator= (const Item&); 126 Item (Item&&); 127 Item& operator= (Item&&); 128 129 /** The menu item's name. */ 130 String text; 131 132 /** The menu item's ID. 133 This must not be 0 if you want the item to be triggerable, but if you're attaching 134 an action callback to the item, you can set the itemID to -1 to indicate that it 135 isn't actively needed. 136 */ 137 int itemID = 0; 138 139 /** An optional function which should be invoked when this menu item is triggered. */ 140 std::function<void()> action; 141 142 /** A sub-menu, or nullptr if there isn't one. */ 143 std::unique_ptr<PopupMenu> subMenu; 144 145 /** A drawable to use as an icon, or nullptr if there isn't one. */ 146 std::unique_ptr<Drawable> image; 147 148 /** A custom component for the item to display, or nullptr if there isn't one. */ 149 ReferenceCountedObjectPtr<CustomComponent> customComponent; 150 151 /** A custom callback for the item to use, or nullptr if there isn't one. */ 152 ReferenceCountedObjectPtr<CustomCallback> customCallback; 153 154 /** A command manager to use to automatically invoke the command, or nullptr if none is specified. */ 155 ApplicationCommandManager* commandManager = nullptr; 156 157 /** An optional string describing the shortcut key for this item. 158 This is only used for displaying at the right-hand edge of a menu item - the 159 menu won't attempt to actually catch or process the key. If you supply a 160 commandManager parameter then the menu will attempt to fill-in this field 161 automatically. 162 */ 163 String shortcutKeyDescription; 164 165 /** A colour to use to draw the menu text. 166 By default this is transparent black, which means that the LookAndFeel should choose the colour. 167 */ 168 Colour colour; 169 170 /** True if this menu item is enabled. */ 171 bool isEnabled = true; 172 173 /** True if this menu item should have a tick mark next to it. */ 174 bool isTicked = false; 175 176 /** True if this menu item is a separator line. */ 177 bool isSeparator = false; 178 179 /** True if this menu item is a section header. */ 180 bool isSectionHeader = false; 181 182 /** Sets the isTicked flag (and returns a reference to this item to allow chaining). */ 183 Item& setTicked (bool shouldBeTicked = true) & noexcept; 184 /** Sets the isEnabled flag (and returns a reference to this item to allow chaining). */ 185 Item& setEnabled (bool shouldBeEnabled) & noexcept; 186 /** Sets the action property (and returns a reference to this item to allow chaining). */ 187 Item& setAction (std::function<void()> action) & noexcept; 188 /** Sets the itemID property (and returns a reference to this item to allow chaining). */ 189 Item& setID (int newID) & noexcept; 190 /** Sets the colour property (and returns a reference to this item to allow chaining). */ 191 Item& setColour (Colour) & noexcept; 192 /** Sets the customComponent property (and returns a reference to this item to allow chaining). */ 193 Item& setCustomComponent (ReferenceCountedObjectPtr<CustomComponent> customComponent) & noexcept; 194 /** Sets the image property (and returns a reference to this item to allow chaining). */ 195 Item& setImage (std::unique_ptr<Drawable>) & noexcept; 196 197 /** Sets the isTicked flag (and returns a reference to this item to allow chaining). */ 198 Item&& setTicked (bool shouldBeTicked = true) && noexcept; 199 /** Sets the isEnabled flag (and returns a reference to this item to allow chaining). */ 200 Item&& setEnabled (bool shouldBeEnabled) && noexcept; 201 /** Sets the action property (and returns a reference to this item to allow chaining). */ 202 Item&& setAction (std::function<void()> action) && noexcept; 203 /** Sets the itemID property (and returns a reference to this item to allow chaining). */ 204 Item&& setID (int newID) && noexcept; 205 /** Sets the colour property (and returns a reference to this item to allow chaining). */ 206 Item&& setColour (Colour) && noexcept; 207 /** Sets the customComponent property (and returns a reference to this item to allow chaining). */ 208 Item&& setCustomComponent (ReferenceCountedObjectPtr<CustomComponent> customComponent) && noexcept; 209 /** Sets the image property (and returns a reference to this item to allow chaining). */ 210 Item&& setImage (std::unique_ptr<Drawable>) && noexcept; 211 }; 212 213 /** Adds an item to the menu. 214 You can call this method for full control over the item that is added, or use the other 215 addItem helper methods if you want to pass arguments rather than creating an Item object. 216 */ 217 void addItem (Item newItem); 218 219 /** Adds an item to the menu with an action callback. */ 220 void addItem (String itemText, 221 std::function<void()> action); 222 223 /** Adds an item to the menu with an action callback. */ 224 void addItem (String itemText, 225 bool isEnabled, 226 bool isTicked, 227 std::function<void()> action); 228 229 /** Appends a new text item for this menu to show. 230 231 @param itemResultID the number that will be returned from the show() method 232 if the user picks this item. The value should never be 233 zero, because that's used to indicate that the user didn't 234 select anything. 235 @param itemText the text to show. 236 @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked 237 @param isTicked if true, the item will be shown with a tick next to it 238 239 @see addSeparator, addColouredItem, addCustomItem, addSubMenu 240 */ 241 void addItem (int itemResultID, 242 String itemText, 243 bool isEnabled = true, 244 bool isTicked = false); 245 246 /** Appends a new item with an icon. 247 248 @param itemResultID the number that will be returned from the show() method 249 if the user picks this item. The value should never be 250 zero, because that's used to indicate that the user didn't 251 select anything. 252 @param itemText the text to show. 253 @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked 254 @param isTicked if true, the item will be shown with a tick next to it 255 @param iconToUse if this is a valid image, it will be displayed to the left of the item. 256 257 @see addSeparator, addColouredItem, addCustomItem, addSubMenu 258 */ 259 void addItem (int itemResultID, 260 String itemText, 261 bool isEnabled, 262 bool isTicked, 263 const Image& iconToUse); 264 265 /** Appends a new item with an icon. 266 267 @param itemResultID the number that will be returned from the show() method 268 if the user picks this item. The value should never be 269 zero, because that's used to indicate that the user didn't 270 select anything. 271 @param itemText the text to show. 272 @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked 273 @param isTicked if true, the item will be shown with a tick next to it 274 @param iconToUse a Drawable object to use as the icon to the left of the item. 275 The menu will take ownership of this drawable object and will 276 delete it later when no longer needed 277 @see addSeparator, addColouredItem, addCustomItem, addSubMenu 278 */ 279 void addItem (int itemResultID, 280 String itemText, 281 bool isEnabled, 282 bool isTicked, 283 std::unique_ptr<Drawable> iconToUse); 284 285 /** Adds an item that represents one of the commands in a command manager object. 286 287 @param commandManager the manager to use to trigger the command and get information 288 about it 289 @param commandID the ID of the command 290 @param displayName if this is non-empty, then this string will be used instead of 291 the command's registered name 292 @param iconToUse an optional Drawable object to use as the icon to the left of the item. 293 The menu will take ownership of this drawable object and will 294 delete it later when no longer needed 295 */ 296 void addCommandItem (ApplicationCommandManager* commandManager, 297 CommandID commandID, 298 String displayName = {}, 299 std::unique_ptr<Drawable> iconToUse = {}); 300 301 /** Appends a text item with a special colour. 302 303 This is the same as addItem(), but specifies a colour to use for the 304 text, which will override the default colours that are used by the 305 current look-and-feel. See addItem() for a description of the parameters. 306 */ 307 void addColouredItem (int itemResultID, 308 String itemText, 309 Colour itemTextColour, 310 bool isEnabled = true, 311 bool isTicked = false, 312 const Image& iconToUse = {}); 313 314 /** Appends a text item with a special colour. 315 316 This is the same as addItem(), but specifies a colour to use for the 317 text, which will override the default colours that are used by the 318 current look-and-feel. See addItem() for a description of the parameters. 319 */ 320 void addColouredItem (int itemResultID, 321 String itemText, 322 Colour itemTextColour, 323 bool isEnabled, 324 bool isTicked, 325 std::unique_ptr<Drawable> iconToUse); 326 327 /** Appends a custom menu item. 328 329 This will add a user-defined component to use as a menu item. 330 331 Note that native macOS menus do not support custom components. 332 333 @see CustomComponent 334 */ 335 void addCustomItem (int itemResultID, 336 std::unique_ptr<CustomComponent> customComponent, 337 std::unique_ptr<const PopupMenu> optionalSubMenu = nullptr); 338 339 /** Appends a custom menu item that can't be used to trigger a result. 340 341 This will add a user-defined component to use as a menu item. 342 The caller must ensure that the passed-in component stays alive 343 until after the menu has been hidden. 344 345 If triggerMenuItemAutomaticallyWhenClicked is true, the menu itself will handle 346 detection of a mouse-click on your component, and use that to trigger the 347 menu ID specified in itemResultID. If this is false, the menu item can't 348 be triggered, so itemResultID is not used. 349 350 Note that native macOS menus do support custom components. 351 */ 352 void addCustomItem (int itemResultID, 353 Component& customComponent, 354 int idealWidth, 355 int idealHeight, 356 bool triggerMenuItemAutomaticallyWhenClicked, 357 std::unique_ptr<const PopupMenu> optionalSubMenu = nullptr); 358 359 /** Appends a sub-menu. 360 361 If the menu that's passed in is empty, it will appear as an inactive item. 362 If the itemResultID argument is non-zero, then the sub-menu item itself can be 363 clicked to trigger it as a command. 364 */ 365 void addSubMenu (String subMenuName, 366 PopupMenu subMenu, 367 bool isEnabled = true); 368 369 /** Appends a sub-menu with an icon. 370 371 If the menu that's passed in is empty, it will appear as an inactive item. 372 If the itemResultID argument is non-zero, then the sub-menu item itself can be 373 clicked to trigger it as a command. 374 */ 375 void addSubMenu (String subMenuName, 376 PopupMenu subMenu, 377 bool isEnabled, 378 const Image& iconToUse, 379 bool isTicked = false, 380 int itemResultID = 0); 381 382 /** Appends a sub-menu with an icon. 383 384 If the menu that's passed in is empty, it will appear as an inactive item. 385 If the itemResultID argument is non-zero, then the sub-menu item itself can be 386 clicked to trigger it as a command. 387 388 The iconToUse parameter is a Drawable object to use as the icon to the left of 389 the item. The menu will take ownership of this drawable object and will delete it 390 later when no longer needed 391 */ 392 void addSubMenu (String subMenuName, 393 PopupMenu subMenu, 394 bool isEnabled, 395 std::unique_ptr<Drawable> iconToUse, 396 bool isTicked = false, 397 int itemResultID = 0); 398 399 /** Appends a separator to the menu, to help break it up into sections. 400 The menu class is smart enough not to display separators at the top or bottom 401 of the menu, and it will replace multiple adjacent separators with a single 402 one, so your code can be quite free and easy about adding these, and it'll 403 always look ok. 404 */ 405 void addSeparator(); 406 407 /** Adds a non-clickable text item to the menu. 408 This is a bold-font items which can be used as a header to separate the items 409 into named groups. 410 */ 411 void addSectionHeader (String title); 412 413 /** Returns the number of items that the menu currently contains. 414 (This doesn't count separators). 415 */ 416 int getNumItems() const noexcept; 417 418 /** Returns true if the menu contains a command item that triggers the given command. */ 419 bool containsCommandItem (int commandID) const; 420 421 /** Returns true if the menu contains any items that can be used. */ 422 bool containsAnyActiveItems() const noexcept; 423 424 //============================================================================== 425 /** Class used to create a set of options to pass to the show() method. 426 You can chain together a series of calls to this class's methods to create 427 a set of whatever options you want to specify. 428 E.g. @code 429 PopupMenu menu; 430 ... 431 menu.showMenu (PopupMenu::Options().withMinimumWidth (100) 432 .withMaximumNumColumns (3) 433 .withTargetComponent (myComp)); 434 @endcode 435 */ 436 class JUCE_API Options 437 { 438 public: 439 Options(); 440 Options (const Options&) = default; 441 Options& operator= (const Options&) = default; 442 443 enum class PopupDirection 444 { 445 upwards, 446 downwards 447 }; 448 449 //============================================================================== 450 Options withTargetComponent (Component* targetComponent) const; 451 Options withTargetComponent (Component& targetComponent) const; 452 Options withTargetScreenArea (Rectangle<int> targetArea) const; 453 Options withDeletionCheck (Component& componentToWatchForDeletion) const; 454 Options withMinimumWidth (int minWidth) const; 455 Options withMinimumNumColumns (int minNumColumns) const; 456 Options withMaximumNumColumns (int maxNumColumns) const; 457 Options withStandardItemHeight (int standardHeight) const; 458 Options withItemThatMustBeVisible (int idOfItemToBeVisible) const; 459 Options withParentComponent (Component* parentComponent) const; 460 Options withPreferredPopupDirection (PopupDirection direction) const; 461 462 //============================================================================== getParentComponent()463 Component* getParentComponent() const noexcept { return parentComponent; } getTargetComponent()464 Component* getTargetComponent() const noexcept { return targetComponent; } hasWatchedComponentBeenDeleted()465 bool hasWatchedComponentBeenDeleted() const noexcept { return isWatchingForDeletion && componentToWatchForDeletion == nullptr; } getTargetScreenArea()466 Rectangle<int> getTargetScreenArea() const noexcept { return targetArea; } getMinimumWidth()467 int getMinimumWidth() const noexcept { return minWidth; } getMaximumNumColumns()468 int getMaximumNumColumns() const noexcept { return maxColumns; } getMinimumNumColumns()469 int getMinimumNumColumns() const noexcept { return minColumns; } getStandardItemHeight()470 int getStandardItemHeight() const noexcept { return standardHeight; } getItemThatMustBeVisible()471 int getItemThatMustBeVisible() const noexcept { return visibleItemID; } getPreferredPopupDirection()472 PopupDirection getPreferredPopupDirection() const noexcept { return preferredPopupDirection; } 473 474 private: 475 //============================================================================== 476 Rectangle<int> targetArea; 477 Component* targetComponent = nullptr; 478 Component* parentComponent = nullptr; 479 WeakReference<Component> componentToWatchForDeletion; 480 int visibleItemID = 0, minWidth = 0, minColumns = 1, maxColumns = 0, standardHeight = 0; 481 bool isWatchingForDeletion = false; 482 PopupDirection preferredPopupDirection = PopupDirection::downwards; 483 }; 484 485 //============================================================================== 486 #if JUCE_MODAL_LOOPS_PERMITTED 487 /** Displays the menu and waits for the user to pick something. 488 489 This will display the menu modally, and return the ID of the item that the 490 user picks. If they click somewhere off the menu to get rid of it without 491 choosing anything, this will return 0. 492 493 The current location of the mouse will be used as the position to show the 494 menu - to explicitly set the menu's position, use showAt() instead. Depending 495 on where this point is on the screen, the menu will appear above, below or 496 to the side of the point. 497 498 @param itemIDThatMustBeVisible if you set this to the ID of one of the menu items, 499 then when the menu first appears, it will make sure 500 that this item is visible. So if the menu has too many 501 items to fit on the screen, it will be scrolled to a 502 position where this item is visible. 503 @param minimumWidth a minimum width for the menu, in pixels. It may be wider 504 than this if some items are too long to fit. 505 @param maximumNumColumns if there are too many items to fit on-screen in a single 506 vertical column, the menu may be laid out as a series of 507 columns - this is the maximum number allowed. To use the 508 default value for this (probably about 7), you can pass 509 in zero. 510 @param standardItemHeight if this is non-zero, it will be used as the standard 511 height for menu items (apart from custom items) 512 @param callback if this is not a nullptr, the menu will be launched 513 asynchronously, returning immediately, and the callback 514 will receive a call when the menu is either dismissed or 515 has an item selected. This object will be owned and 516 deleted by the system, so make sure that it works safely 517 and that any pointers that it uses are safely within scope. 518 @see showAt 519 */ 520 int show (int itemIDThatMustBeVisible = 0, 521 int minimumWidth = 0, 522 int maximumNumColumns = 0, 523 int standardItemHeight = 0, 524 ModalComponentManager::Callback* callback = nullptr); 525 526 527 /** Displays the menu at a specific location. 528 529 This is the same as show(), but uses a specific location (in global screen 530 coordinates) rather than the current mouse position. 531 532 The screenAreaToAttachTo parameter indicates a screen area to which the menu 533 will be adjacent. Depending on where this is, the menu will decide which edge to 534 attach itself to, in order to fit itself fully on-screen. If you just want to 535 trigger a menu at a specific point, you can pass in a rectangle of size (0, 0) 536 with the position that you want. 537 538 @see show() 539 */ 540 int showAt (Rectangle<int> screenAreaToAttachTo, 541 int itemIDThatMustBeVisible = 0, 542 int minimumWidth = 0, 543 int maximumNumColumns = 0, 544 int standardItemHeight = 0, 545 ModalComponentManager::Callback* callback = nullptr); 546 547 /** Displays the menu as if it's attached to a component such as a button. 548 549 This is similar to showAt(), but will position it next to the given component, e.g. 550 so that the menu's edge is aligned with that of the component. This is intended for 551 things like buttons that trigger a pop-up menu. 552 */ 553 int showAt (Component* componentToAttachTo, 554 int itemIDThatMustBeVisible = 0, 555 int minimumWidth = 0, 556 int maximumNumColumns = 0, 557 int standardItemHeight = 0, 558 ModalComponentManager::Callback* callback = nullptr); 559 560 /** Displays and runs the menu modally, with a set of options. 561 */ 562 int showMenu (const Options& options); 563 #endif 564 565 /** Runs the menu asynchronously. */ 566 void showMenuAsync (const Options& options); 567 568 /** Runs the menu asynchronously, with a user-provided callback that will receive the result. */ 569 void showMenuAsync (const Options& options, 570 ModalComponentManager::Callback* callback); 571 572 /** Runs the menu asynchronously, with a user-provided callback that will receive the result. */ 573 void showMenuAsync (const Options& options, 574 std::function<void (int)> callback); 575 576 //============================================================================== 577 /** Closes any menus that are currently open. 578 579 This might be useful if you have a situation where your window is being closed 580 by some means other than a user action, and you'd like to make sure that menus 581 aren't left hanging around. 582 */ 583 static bool JUCE_CALLTYPE dismissAllActiveMenus(); 584 585 586 //============================================================================== 587 /** Specifies a look-and-feel for the menu and any sub-menus that it has. 588 589 This can be called before show() if you need a customised menu. Be careful 590 not to delete the LookAndFeel object before the menu has been deleted. 591 */ 592 void setLookAndFeel (LookAndFeel* newLookAndFeel); 593 594 //============================================================================== 595 /** A set of colour IDs to use to change the colour of various aspects of the menu. 596 597 These constants can be used either via the LookAndFeel::setColour() 598 method for the look and feel that is set for this menu with setLookAndFeel() 599 600 @see setLookAndFeel, LookAndFeel::setColour, LookAndFeel::findColour 601 */ 602 enum ColourIds 603 { 604 backgroundColourId = 0x1000700, /**< The colour to fill the menu's background with. */ 605 textColourId = 0x1000600, /**< The colour for normal menu item text, (unless the 606 colour is specified when the item is added). */ 607 headerTextColourId = 0x1000601, /**< The colour for section header item text (see the 608 addSectionHeader() method). */ 609 highlightedBackgroundColourId = 0x1000900, /**< The colour to fill the background of the currently 610 highlighted menu item. */ 611 highlightedTextColourId = 0x1000800, /**< The colour to use for the text of the currently 612 highlighted item. */ 613 }; 614 615 //============================================================================== 616 /** 617 Allows you to iterate through the items in a pop-up menu, and examine 618 their properties. 619 620 To use this, just create one and repeatedly call its next() method. When this 621 returns true, all the member variables of the iterator are filled-out with 622 information describing the menu item. When it returns false, the end of the 623 list has been reached. 624 */ 625 class JUCE_API MenuItemIterator 626 { 627 public: 628 //============================================================================== 629 /** Creates an iterator that will scan through the items in the specified 630 menu. 631 632 Be careful not to add any items to a menu while it is being iterated, 633 or things could get out of step. 634 635 @param menu the menu that needs to be scanned 636 637 @param searchRecursively if true, all submenus will be recursed into to 638 do an exhaustive search 639 */ 640 MenuItemIterator (const PopupMenu& menu, bool searchRecursively = false); 641 642 /** Destructor. */ 643 ~MenuItemIterator(); 644 645 /** Returns true if there is another item, and sets up all this object's 646 member variables to reflect that item's properties. 647 */ 648 bool next(); 649 650 /** Returns a reference to the description of the current item. 651 It is only valid to call this after next() has returned true! 652 */ 653 Item& getItem() const; 654 655 private: 656 //============================================================================== 657 bool searchRecursively; 658 659 Array<int> index; 660 Array<const PopupMenu*> menus; 661 PopupMenu::Item* currentItem = nullptr; 662 663 MenuItemIterator& operator= (const MenuItemIterator&); 664 JUCE_LEAK_DETECTOR (MenuItemIterator) 665 }; 666 667 //============================================================================== 668 /** A user-defined component that can be used as an item in a popup menu. 669 @see PopupMenu::addCustomItem 670 */ 671 class JUCE_API CustomComponent : public Component, 672 public SingleThreadedReferenceCountedObject 673 { 674 public: 675 /** Creates a custom item. 676 If isTriggeredAutomatically is true, then the menu will automatically detect 677 a mouse-click on this component and use that to invoke the menu item. If it's 678 false, then it's up to your class to manually trigger the item when it wants to. 679 */ 680 CustomComponent (bool isTriggeredAutomatically = true); 681 682 /** Destructor. */ 683 ~CustomComponent() override; 684 685 /** Returns a rectangle with the size that this component would like to have. 686 687 Note that the size which this method returns isn't necessarily the one that 688 the menu will give it, as the items will be stretched to have a uniform width. 689 */ 690 virtual void getIdealSize (int& idealWidth, int& idealHeight) = 0; 691 692 /** Dismisses the menu, indicating that this item has been chosen. 693 694 This will cause the menu to exit from its modal state, returning 695 this item's id as the result. 696 */ 697 void triggerMenuItem(); 698 699 /** Returns true if this item should be highlighted because the mouse is over it. 700 You can call this method in your paint() method to find out whether 701 to draw a highlight. 702 */ isItemHighlighted()703 bool isItemHighlighted() const noexcept { return isHighlighted; } 704 705 /** Returns a pointer to the Item that holds this custom component, if this 706 component is currently held by an Item. 707 You can query the Item for information that you might want to use 708 in your paint() method, such as the item's enabled and ticked states. 709 */ getItem()710 const PopupMenu::Item* getItem() const noexcept { return item; } 711 712 /** @internal */ isTriggeredAutomatically()713 bool isTriggeredAutomatically() const noexcept { return triggeredAutomatically; } 714 /** @internal */ 715 void setHighlighted (bool shouldBeHighlighted); 716 717 private: 718 //============================================================================== 719 bool isHighlighted = false, triggeredAutomatically; 720 const PopupMenu::Item* item = nullptr; 721 722 friend PopupMenu; 723 724 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomComponent) 725 }; 726 727 //============================================================================== 728 /** A user-defined callback that can be used for specific items in a popup menu. 729 @see PopupMenu::Item::customCallback 730 */ 731 class JUCE_API CustomCallback : public SingleThreadedReferenceCountedObject 732 { 733 public: 734 CustomCallback(); 735 ~CustomCallback() override; 736 737 /** Callback to indicate this item has been triggered. 738 @returns true if the itemID should be sent to the exitModalState method, or 739 false if it should send 0, indicating no further action should be taken 740 */ 741 virtual bool menuItemTriggered() = 0; 742 743 private: 744 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomCallback) 745 }; 746 747 //============================================================================== 748 /** This abstract base class is implemented by LookAndFeel classes to provide 749 menu drawing functionality. 750 */ 751 struct JUCE_API LookAndFeelMethods 752 { 753 virtual ~LookAndFeelMethods() = default; 754 755 /** Fills the background of a popup menu component. */ 756 virtual void drawPopupMenuBackground (Graphics&, int width, int height) = 0; 757 758 /** Draws one of the items in a popup menu. */ 759 virtual void drawPopupMenuItem (Graphics&, const Rectangle<int>& area, 760 bool isSeparator, bool isActive, bool isHighlighted, 761 bool isTicked, bool hasSubMenu, 762 const String& text, 763 const String& shortcutKeyText, 764 const Drawable* icon, 765 const Colour* textColour) = 0; 766 767 virtual void drawPopupMenuSectionHeader (Graphics&, const Rectangle<int>& area, 768 const String& sectionName) = 0; 769 770 /** Returns the size and style of font to use in popup menus. */ 771 virtual Font getPopupMenuFont() = 0; 772 773 virtual void drawPopupMenuUpDownArrow (Graphics&, 774 int width, int height, 775 bool isScrollUpArrow) = 0; 776 777 /** Finds the best size for an item in a popup menu. */ 778 virtual void getIdealPopupMenuItemSize (const String& text, 779 bool isSeparator, 780 int standardMenuItemHeight, 781 int& idealWidth, 782 int& idealHeight) = 0; 783 784 virtual int getMenuWindowFlags() = 0; 785 786 virtual void drawMenuBarBackground (Graphics&, int width, int height, 787 bool isMouseOverBar, 788 MenuBarComponent&) = 0; 789 790 virtual int getDefaultMenuBarHeight() = 0; 791 792 virtual int getMenuBarItemWidth (MenuBarComponent&, int itemIndex, const String& itemText) = 0; 793 794 virtual Font getMenuBarFont (MenuBarComponent&, int itemIndex, const String& itemText) = 0; 795 796 virtual void drawMenuBarItem (Graphics&, int width, int height, 797 int itemIndex, 798 const String& itemText, 799 bool isMouseOverItem, 800 bool isMenuOpen, 801 bool isMouseOverBar, 802 MenuBarComponent&) = 0; 803 804 virtual Component* getParentComponentForMenuOptions (const PopupMenu::Options& options) = 0; 805 806 virtual void preparePopupMenuWindow (Component& newWindow) = 0; 807 808 /** Return true if you want your popup menus to scale with the target component's AffineTransform 809 or scale factor */ 810 virtual bool shouldPopupMenuScaleWithTargetComponent (const PopupMenu::Options& options) = 0; 811 812 virtual int getPopupMenuBorderSize() = 0; 813 }; 814 815 private: 816 //============================================================================== 817 JUCE_PUBLIC_IN_DLL_BUILD (struct HelperClasses) 818 class Window; 819 friend struct HelperClasses; 820 friend class MenuBarComponent; 821 822 Array<Item> items; 823 WeakReference<LookAndFeel> lookAndFeel; 824 825 Component* createWindow (const Options&, ApplicationCommandManager**) const; 826 int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool); 827 828 static void setItem (CustomComponent&, const Item*); 829 830 #if JUCE_CATCH_DEPRECATED_CODE_MISUSE 831 // These methods have new implementations now - see its new definition drawPopupMenuItem(Graphics &,int,int,bool,bool,bool,bool,bool,const String &,const String &,Image *,const Colour *)832 int drawPopupMenuItem (Graphics&, int, int, bool, bool, bool, bool, bool, const String&, const String&, Image*, const Colour*) { return 0; } 833 #endif 834 835 JUCE_LEAK_DETECTOR (PopupMenu) 836 }; 837 838 } // namespace juce 839