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 /** True if this is the final item in the current column. */ 183 bool shouldBreakAfter = false; 184 185 /** Sets the isTicked flag (and returns a reference to this item to allow chaining). */ 186 Item& setTicked (bool shouldBeTicked = true) & noexcept; 187 /** Sets the isEnabled flag (and returns a reference to this item to allow chaining). */ 188 Item& setEnabled (bool shouldBeEnabled) & noexcept; 189 /** Sets the action property (and returns a reference to this item to allow chaining). */ 190 Item& setAction (std::function<void()> action) & noexcept; 191 /** Sets the itemID property (and returns a reference to this item to allow chaining). */ 192 Item& setID (int newID) & noexcept; 193 /** Sets the colour property (and returns a reference to this item to allow chaining). */ 194 Item& setColour (Colour) & noexcept; 195 /** Sets the customComponent property (and returns a reference to this item to allow chaining). */ 196 Item& setCustomComponent (ReferenceCountedObjectPtr<CustomComponent> customComponent) & noexcept; 197 /** Sets the image property (and returns a reference to this item to allow chaining). */ 198 Item& setImage (std::unique_ptr<Drawable>) & noexcept; 199 200 /** Sets the isTicked flag (and returns a reference to this item to allow chaining). */ 201 Item&& setTicked (bool shouldBeTicked = true) && noexcept; 202 /** Sets the isEnabled flag (and returns a reference to this item to allow chaining). */ 203 Item&& setEnabled (bool shouldBeEnabled) && noexcept; 204 /** Sets the action property (and returns a reference to this item to allow chaining). */ 205 Item&& setAction (std::function<void()> action) && noexcept; 206 /** Sets the itemID property (and returns a reference to this item to allow chaining). */ 207 Item&& setID (int newID) && noexcept; 208 /** Sets the colour property (and returns a reference to this item to allow chaining). */ 209 Item&& setColour (Colour) && noexcept; 210 /** Sets the customComponent property (and returns a reference to this item to allow chaining). */ 211 Item&& setCustomComponent (ReferenceCountedObjectPtr<CustomComponent> customComponent) && noexcept; 212 /** Sets the image property (and returns a reference to this item to allow chaining). */ 213 Item&& setImage (std::unique_ptr<Drawable>) && noexcept; 214 }; 215 216 /** Adds an item to the menu. 217 You can call this method for full control over the item that is added, or use the other 218 addItem helper methods if you want to pass arguments rather than creating an Item object. 219 */ 220 void addItem (Item newItem); 221 222 /** Adds an item to the menu with an action callback. */ 223 void addItem (String itemText, 224 std::function<void()> action); 225 226 /** Adds an item to the menu with an action callback. */ 227 void addItem (String itemText, 228 bool isEnabled, 229 bool isTicked, 230 std::function<void()> action); 231 232 /** Appends a new text item for this menu to show. 233 234 @param itemResultID the number that will be returned from the show() method 235 if the user picks this item. The value should never be 236 zero, because that's used to indicate that the user didn't 237 select anything. 238 @param itemText the text to show. 239 @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked 240 @param isTicked if true, the item will be shown with a tick next to it 241 242 @see addSeparator, addColouredItem, addCustomItem, addSubMenu 243 */ 244 void addItem (int itemResultID, 245 String itemText, 246 bool isEnabled = true, 247 bool isTicked = false); 248 249 /** Appends a new item with an icon. 250 251 @param itemResultID the number that will be returned from the show() method 252 if the user picks this item. The value should never be 253 zero, because that's used to indicate that the user didn't 254 select anything. 255 @param itemText the text to show. 256 @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked 257 @param isTicked if true, the item will be shown with a tick next to it 258 @param iconToUse if this is a valid image, it will be displayed to the left of the item. 259 260 @see addSeparator, addColouredItem, addCustomItem, addSubMenu 261 */ 262 void addItem (int itemResultID, 263 String itemText, 264 bool isEnabled, 265 bool isTicked, 266 const Image& iconToUse); 267 268 /** Appends a new item with an icon. 269 270 @param itemResultID the number that will be returned from the show() method 271 if the user picks this item. The value should never be 272 zero, because that's used to indicate that the user didn't 273 select anything. 274 @param itemText the text to show. 275 @param isEnabled if false, the item will be shown 'greyed-out' and can't be picked 276 @param isTicked if true, the item will be shown with a tick next to it 277 @param iconToUse a Drawable object to use as the icon to the left of the item. 278 The menu will take ownership of this drawable object and will 279 delete it later when no longer needed 280 @see addSeparator, addColouredItem, addCustomItem, addSubMenu 281 */ 282 void addItem (int itemResultID, 283 String itemText, 284 bool isEnabled, 285 bool isTicked, 286 std::unique_ptr<Drawable> iconToUse); 287 288 /** Adds an item that represents one of the commands in a command manager object. 289 290 @param commandManager the manager to use to trigger the command and get information 291 about it 292 @param commandID the ID of the command 293 @param displayName if this is non-empty, then this string will be used instead of 294 the command's registered name 295 @param iconToUse an optional Drawable object to use as the icon to the left of the item. 296 The menu will take ownership of this drawable object and will 297 delete it later when no longer needed 298 */ 299 void addCommandItem (ApplicationCommandManager* commandManager, 300 CommandID commandID, 301 String displayName = {}, 302 std::unique_ptr<Drawable> iconToUse = {}); 303 304 /** Appends a text item with a special colour. 305 306 This is the same as addItem(), but specifies a colour to use for the 307 text, which will override the default colours that are used by the 308 current look-and-feel. See addItem() for a description of the parameters. 309 */ 310 void addColouredItem (int itemResultID, 311 String itemText, 312 Colour itemTextColour, 313 bool isEnabled = true, 314 bool isTicked = false, 315 const Image& iconToUse = {}); 316 317 /** Appends a text item with a special colour. 318 319 This is the same as addItem(), but specifies a colour to use for the 320 text, which will override the default colours that are used by the 321 current look-and-feel. See addItem() for a description of the parameters. 322 */ 323 void addColouredItem (int itemResultID, 324 String itemText, 325 Colour itemTextColour, 326 bool isEnabled, 327 bool isTicked, 328 std::unique_ptr<Drawable> iconToUse); 329 330 /** Appends a custom menu item. 331 332 This will add a user-defined component to use as a menu item. 333 334 Note that native macOS menus do not support custom components. 335 336 @see CustomComponent 337 */ 338 void addCustomItem (int itemResultID, 339 std::unique_ptr<CustomComponent> customComponent, 340 std::unique_ptr<const PopupMenu> optionalSubMenu = nullptr); 341 342 /** Appends a custom menu item that can't be used to trigger a result. 343 344 This will add a user-defined component to use as a menu item. 345 The caller must ensure that the passed-in component stays alive 346 until after the menu has been hidden. 347 348 If triggerMenuItemAutomaticallyWhenClicked is true, the menu itself will handle 349 detection of a mouse-click on your component, and use that to trigger the 350 menu ID specified in itemResultID. If this is false, the menu item can't 351 be triggered, so itemResultID is not used. 352 353 Note that native macOS menus do support custom components. 354 */ 355 void addCustomItem (int itemResultID, 356 Component& customComponent, 357 int idealWidth, 358 int idealHeight, 359 bool triggerMenuItemAutomaticallyWhenClicked, 360 std::unique_ptr<const PopupMenu> optionalSubMenu = nullptr); 361 362 /** Appends a sub-menu. 363 364 If the menu that's passed in is empty, it will appear as an inactive item. 365 If the itemResultID argument is non-zero, then the sub-menu item itself can be 366 clicked to trigger it as a command. 367 */ 368 void addSubMenu (String subMenuName, 369 PopupMenu subMenu, 370 bool isEnabled = true); 371 372 /** Appends a sub-menu with an icon. 373 374 If the menu that's passed in is empty, it will appear as an inactive item. 375 If the itemResultID argument is non-zero, then the sub-menu item itself can be 376 clicked to trigger it as a command. 377 */ 378 void addSubMenu (String subMenuName, 379 PopupMenu subMenu, 380 bool isEnabled, 381 const Image& iconToUse, 382 bool isTicked = false, 383 int itemResultID = 0); 384 385 /** Appends a sub-menu with an icon. 386 387 If the menu that's passed in is empty, it will appear as an inactive item. 388 If the itemResultID argument is non-zero, then the sub-menu item itself can be 389 clicked to trigger it as a command. 390 391 The iconToUse parameter is a Drawable object to use as the icon to the left of 392 the item. The menu will take ownership of this drawable object and will delete it 393 later when no longer needed 394 */ 395 void addSubMenu (String subMenuName, 396 PopupMenu subMenu, 397 bool isEnabled, 398 std::unique_ptr<Drawable> iconToUse, 399 bool isTicked = false, 400 int itemResultID = 0); 401 402 /** Appends a separator to the menu, to help break it up into sections. 403 The menu class is smart enough not to display separators at the top or bottom 404 of the menu, and it will replace multiple adjacent separators with a single 405 one, so your code can be quite free and easy about adding these, and it'll 406 always look ok. 407 */ 408 void addSeparator(); 409 410 /** Adds a non-clickable text item to the menu. 411 This is a bold-font items which can be used as a header to separate the items 412 into named groups. 413 */ 414 void addSectionHeader (String title); 415 416 /** Adds a column break to the menu, to help break it up into sections. 417 Subsequent items will be placed in a new column, rather than being appended 418 to the current column. 419 420 If a menu contains explicit column breaks, the menu will never add additional 421 breaks. 422 */ 423 void addColumnBreak(); 424 425 /** Returns the number of items that the menu currently contains. 426 (This doesn't count separators). 427 */ 428 int getNumItems() const noexcept; 429 430 /** Returns true if the menu contains a command item that triggers the given command. */ 431 bool containsCommandItem (int commandID) const; 432 433 /** Returns true if the menu contains any items that can be used. */ 434 bool containsAnyActiveItems() const noexcept; 435 436 //============================================================================== 437 /** Class used to create a set of options to pass to the show() method. 438 You can chain together a series of calls to this class's methods to create 439 a set of whatever options you want to specify. 440 E.g. @code 441 PopupMenu menu; 442 ... 443 menu.showMenu (PopupMenu::Options().withMinimumWidth (100) 444 .withMaximumNumColumns (3) 445 .withTargetComponent (myComp)); 446 @endcode 447 */ 448 class JUCE_API Options 449 { 450 public: 451 Options(); 452 Options (const Options&) = default; 453 Options& operator= (const Options&) = default; 454 455 enum class PopupDirection 456 { 457 upwards, 458 downwards 459 }; 460 461 //============================================================================== 462 Options withTargetComponent (Component* targetComponent) const; 463 Options withTargetComponent (Component& targetComponent) const; 464 Options withTargetScreenArea (Rectangle<int> targetArea) const; 465 Options withDeletionCheck (Component& componentToWatchForDeletion) const; 466 Options withMinimumWidth (int minWidth) const; 467 Options withMinimumNumColumns (int minNumColumns) const; 468 Options withMaximumNumColumns (int maxNumColumns) const; 469 Options withStandardItemHeight (int standardHeight) const; 470 Options withItemThatMustBeVisible (int idOfItemToBeVisible) const; 471 Options withParentComponent (Component* parentComponent) const; 472 Options withPreferredPopupDirection (PopupDirection direction) const; 473 Options withInitiallySelectedItem (int idOfItemToBeSelected) const; 474 475 //============================================================================== getParentComponent()476 Component* getParentComponent() const noexcept { return parentComponent; } getTargetComponent()477 Component* getTargetComponent() const noexcept { return targetComponent; } hasWatchedComponentBeenDeleted()478 bool hasWatchedComponentBeenDeleted() const noexcept { return isWatchingForDeletion && componentToWatchForDeletion == nullptr; } getTargetScreenArea()479 Rectangle<int> getTargetScreenArea() const noexcept { return targetArea; } getMinimumWidth()480 int getMinimumWidth() const noexcept { return minWidth; } getMaximumNumColumns()481 int getMaximumNumColumns() const noexcept { return maxColumns; } getMinimumNumColumns()482 int getMinimumNumColumns() const noexcept { return minColumns; } getStandardItemHeight()483 int getStandardItemHeight() const noexcept { return standardHeight; } getItemThatMustBeVisible()484 int getItemThatMustBeVisible() const noexcept { return visibleItemID; } getPreferredPopupDirection()485 PopupDirection getPreferredPopupDirection() const noexcept { return preferredPopupDirection; } getInitiallySelectedItemId()486 int getInitiallySelectedItemId() const noexcept { return initiallySelectedItemId; } 487 488 private: 489 //============================================================================== 490 Rectangle<int> targetArea; 491 Component* targetComponent = nullptr; 492 Component* parentComponent = nullptr; 493 WeakReference<Component> componentToWatchForDeletion; 494 int visibleItemID = 0, minWidth = 0, minColumns = 1, maxColumns = 0, standardHeight = 0, initiallySelectedItemId = 0; 495 bool isWatchingForDeletion = false; 496 PopupDirection preferredPopupDirection = PopupDirection::downwards; 497 }; 498 499 //============================================================================== 500 #if JUCE_MODAL_LOOPS_PERMITTED || DOXYGEN 501 /** Displays the menu and waits for the user to pick something. 502 503 This will display the menu modally, and return the ID of the item that the 504 user picks. If they click somewhere off the menu to get rid of it without 505 choosing anything, this will return 0. 506 507 The current location of the mouse will be used as the position to show the 508 menu - to explicitly set the menu's position, use showAt() instead. Depending 509 on where this point is on the screen, the menu will appear above, below or 510 to the side of the point. 511 512 @param itemIDThatMustBeVisible if you set this to the ID of one of the menu items, 513 then when the menu first appears, it will make sure 514 that this item is visible. So if the menu has too many 515 items to fit on the screen, it will be scrolled to a 516 position where this item is visible. 517 @param minimumWidth a minimum width for the menu, in pixels. It may be wider 518 than this if some items are too long to fit. 519 @param maximumNumColumns if there are too many items to fit on-screen in a single 520 vertical column, the menu may be laid out as a series of 521 columns - this is the maximum number allowed. To use the 522 default value for this (probably about 7), you can pass 523 in zero. 524 @param standardItemHeight if this is non-zero, it will be used as the standard 525 height for menu items (apart from custom items) 526 @param callback if this is not a nullptr, the menu will be launched 527 asynchronously, returning immediately, and the callback 528 will receive a call when the menu is either dismissed or 529 has an item selected. This object will be owned and 530 deleted by the system, so make sure that it works safely 531 and that any pointers that it uses are safely within scope. 532 @see showAt 533 */ 534 int show (int itemIDThatMustBeVisible = 0, 535 int minimumWidth = 0, 536 int maximumNumColumns = 0, 537 int standardItemHeight = 0, 538 ModalComponentManager::Callback* callback = nullptr); 539 540 541 /** Displays the menu at a specific location. 542 543 This is the same as show(), but uses a specific location (in global screen 544 coordinates) rather than the current mouse position. 545 546 The screenAreaToAttachTo parameter indicates a screen area to which the menu 547 will be adjacent. Depending on where this is, the menu will decide which edge to 548 attach itself to, in order to fit itself fully on-screen. If you just want to 549 trigger a menu at a specific point, you can pass in a rectangle of size (0, 0) 550 with the position that you want. 551 552 @see show() 553 */ 554 int showAt (Rectangle<int> screenAreaToAttachTo, 555 int itemIDThatMustBeVisible = 0, 556 int minimumWidth = 0, 557 int maximumNumColumns = 0, 558 int standardItemHeight = 0, 559 ModalComponentManager::Callback* callback = nullptr); 560 561 /** Displays the menu as if it's attached to a component such as a button. 562 563 This is similar to showAt(), but will position it next to the given component, e.g. 564 so that the menu's edge is aligned with that of the component. This is intended for 565 things like buttons that trigger a pop-up menu. 566 */ 567 int showAt (Component* componentToAttachTo, 568 int itemIDThatMustBeVisible = 0, 569 int minimumWidth = 0, 570 int maximumNumColumns = 0, 571 int standardItemHeight = 0, 572 ModalComponentManager::Callback* callback = nullptr); 573 574 /** Displays and runs the menu modally, with a set of options. 575 */ 576 int showMenu (const Options& options); 577 #endif 578 579 /** Runs the menu asynchronously. */ 580 void showMenuAsync (const Options& options); 581 582 /** Runs the menu asynchronously, with a user-provided callback that will receive the result. */ 583 void showMenuAsync (const Options& options, 584 ModalComponentManager::Callback* callback); 585 586 /** Runs the menu asynchronously, with a user-provided callback that will receive the result. */ 587 void showMenuAsync (const Options& options, 588 std::function<void (int)> callback); 589 590 //============================================================================== 591 /** Closes any menus that are currently open. 592 593 This might be useful if you have a situation where your window is being closed 594 by some means other than a user action, and you'd like to make sure that menus 595 aren't left hanging around. 596 */ 597 static bool JUCE_CALLTYPE dismissAllActiveMenus(); 598 599 600 //============================================================================== 601 /** Specifies a look-and-feel for the menu and any sub-menus that it has. 602 603 This can be called before show() if you need a customised menu. Be careful 604 not to delete the LookAndFeel object before the menu has been deleted. 605 */ 606 void setLookAndFeel (LookAndFeel* newLookAndFeel); 607 608 //============================================================================== 609 /** A set of colour IDs to use to change the colour of various aspects of the menu. 610 611 These constants can be used either via the LookAndFeel::setColour() 612 method for the look and feel that is set for this menu with setLookAndFeel() 613 614 @see setLookAndFeel, LookAndFeel::setColour, LookAndFeel::findColour 615 */ 616 enum ColourIds 617 { 618 backgroundColourId = 0x1000700, /**< The colour to fill the menu's background with. */ 619 textColourId = 0x1000600, /**< The colour for normal menu item text, (unless the 620 colour is specified when the item is added). */ 621 headerTextColourId = 0x1000601, /**< The colour for section header item text (see the 622 addSectionHeader() method). */ 623 highlightedBackgroundColourId = 0x1000900, /**< The colour to fill the background of the currently 624 highlighted menu item. */ 625 highlightedTextColourId = 0x1000800, /**< The colour to use for the text of the currently 626 highlighted item. */ 627 }; 628 629 //============================================================================== 630 /** 631 Allows you to iterate through the items in a pop-up menu, and examine 632 their properties. 633 634 To use this, just create one and repeatedly call its next() method. When this 635 returns true, all the member variables of the iterator are filled-out with 636 information describing the menu item. When it returns false, the end of the 637 list has been reached. 638 */ 639 class JUCE_API MenuItemIterator 640 { 641 public: 642 //============================================================================== 643 /** Creates an iterator that will scan through the items in the specified 644 menu. 645 646 Be careful not to add any items to a menu while it is being iterated, 647 or things could get out of step. 648 649 @param menu the menu that needs to be scanned 650 651 @param searchRecursively if true, all submenus will be recursed into to 652 do an exhaustive search 653 */ 654 MenuItemIterator (const PopupMenu& menu, bool searchRecursively = false); 655 656 /** Destructor. */ 657 ~MenuItemIterator(); 658 659 /** Returns true if there is another item, and sets up all this object's 660 member variables to reflect that item's properties. 661 */ 662 bool next(); 663 664 /** Returns a reference to the description of the current item. 665 It is only valid to call this after next() has returned true! 666 */ 667 Item& getItem() const; 668 669 private: 670 //============================================================================== 671 bool searchRecursively; 672 673 Array<int> index; 674 Array<const PopupMenu*> menus; 675 PopupMenu::Item* currentItem = nullptr; 676 677 MenuItemIterator& operator= (const MenuItemIterator&); 678 JUCE_LEAK_DETECTOR (MenuItemIterator) 679 }; 680 681 //============================================================================== 682 /** A user-defined component that can be used as an item in a popup menu. 683 @see PopupMenu::addCustomItem 684 */ 685 class JUCE_API CustomComponent : public Component, 686 public SingleThreadedReferenceCountedObject 687 { 688 public: 689 /** Creates a custom item. 690 If isTriggeredAutomatically is true, then the menu will automatically detect 691 a mouse-click on this component and use that to invoke the menu item. If it's 692 false, then it's up to your class to manually trigger the item when it wants to. 693 */ 694 CustomComponent (bool isTriggeredAutomatically = true); 695 696 /** Destructor. */ 697 ~CustomComponent() override; 698 699 /** Returns a rectangle with the size that this component would like to have. 700 701 Note that the size which this method returns isn't necessarily the one that 702 the menu will give it, as the items will be stretched to have a uniform width. 703 */ 704 virtual void getIdealSize (int& idealWidth, int& idealHeight) = 0; 705 706 /** Dismisses the menu, indicating that this item has been chosen. 707 708 This will cause the menu to exit from its modal state, returning 709 this item's id as the result. 710 */ 711 void triggerMenuItem(); 712 713 /** Returns true if this item should be highlighted because the mouse is over it. 714 You can call this method in your paint() method to find out whether 715 to draw a highlight. 716 */ isItemHighlighted()717 bool isItemHighlighted() const noexcept { return isHighlighted; } 718 719 /** Returns a pointer to the Item that holds this custom component, if this 720 component is currently held by an Item. 721 You can query the Item for information that you might want to use 722 in your paint() method, such as the item's enabled and ticked states. 723 */ getItem()724 const PopupMenu::Item* getItem() const noexcept { return item; } 725 726 /** @internal */ isTriggeredAutomatically()727 bool isTriggeredAutomatically() const noexcept { return triggeredAutomatically; } 728 /** @internal */ 729 void setHighlighted (bool shouldBeHighlighted); 730 731 private: 732 //============================================================================== 733 bool isHighlighted = false, triggeredAutomatically; 734 const PopupMenu::Item* item = nullptr; 735 736 friend PopupMenu; 737 738 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomComponent) 739 }; 740 741 //============================================================================== 742 /** A user-defined callback that can be used for specific items in a popup menu. 743 @see PopupMenu::Item::customCallback 744 */ 745 class JUCE_API CustomCallback : public SingleThreadedReferenceCountedObject 746 { 747 public: 748 CustomCallback(); 749 ~CustomCallback() override; 750 751 /** Callback to indicate this item has been triggered. 752 @returns true if the itemID should be sent to the exitModalState method, or 753 false if it should send 0, indicating no further action should be taken 754 */ 755 virtual bool menuItemTriggered() = 0; 756 757 private: 758 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomCallback) 759 }; 760 761 //============================================================================== 762 /** This abstract base class is implemented by LookAndFeel classes to provide 763 menu drawing functionality. 764 */ 765 struct JUCE_API LookAndFeelMethods 766 { 767 virtual ~LookAndFeelMethods() = default; 768 769 /** Fills the background of a popup menu component. */ 770 virtual void drawPopupMenuBackground (Graphics&, int width, int height); 771 772 /** Fills the background of a popup menu component. */ 773 virtual void drawPopupMenuBackgroundWithOptions (Graphics&, 774 int width, 775 int height, 776 const Options&) = 0; 777 778 /** Draws one of the items in a popup menu. */ 779 virtual void drawPopupMenuItem (Graphics&, const Rectangle<int>& area, 780 bool isSeparator, bool isActive, bool isHighlighted, 781 bool isTicked, bool hasSubMenu, 782 const String& text, 783 const String& shortcutKeyText, 784 const Drawable* icon, 785 const Colour* textColour); 786 787 /** Draws one of the items in a popup menu. */ 788 virtual void drawPopupMenuItemWithOptions (Graphics&, const Rectangle<int>& area, 789 bool isHighlighted, 790 const Item& item, 791 const Options&) = 0; 792 793 virtual void drawPopupMenuSectionHeader (Graphics&, const Rectangle<int>&, 794 const String&); 795 796 virtual void drawPopupMenuSectionHeaderWithOptions (Graphics&, const Rectangle<int>& area, 797 const String& sectionName, 798 const Options&) = 0; 799 800 /** Returns the size and style of font to use in popup menus. */ 801 virtual Font getPopupMenuFont() = 0; 802 803 virtual void drawPopupMenuUpDownArrow (Graphics&, 804 int width, int height, 805 bool isScrollUpArrow); 806 807 virtual void drawPopupMenuUpDownArrowWithOptions (Graphics&, 808 int width, int height, 809 bool isScrollUpArrow, 810 const Options&) = 0; 811 812 /** Finds the best size for an item in a popup menu. */ 813 virtual void getIdealPopupMenuItemSize (const String& text, 814 bool isSeparator, 815 int standardMenuItemHeight, 816 int& idealWidth, 817 int& idealHeight); 818 819 /** Finds the best size for an item in a popup menu. */ 820 virtual void getIdealPopupMenuItemSizeWithOptions (const String& text, 821 bool isSeparator, 822 int standardMenuItemHeight, 823 int& idealWidth, 824 int& idealHeight, 825 const Options&) = 0; 826 827 virtual int getMenuWindowFlags() = 0; 828 829 virtual void drawMenuBarBackground (Graphics&, int width, int height, 830 bool isMouseOverBar, 831 MenuBarComponent&) = 0; 832 833 virtual int getDefaultMenuBarHeight() = 0; 834 835 virtual int getMenuBarItemWidth (MenuBarComponent&, int itemIndex, const String& itemText) = 0; 836 837 virtual Font getMenuBarFont (MenuBarComponent&, int itemIndex, const String& itemText) = 0; 838 839 virtual void drawMenuBarItem (Graphics&, int width, int height, 840 int itemIndex, 841 const String& itemText, 842 bool isMouseOverItem, 843 bool isMenuOpen, 844 bool isMouseOverBar, 845 MenuBarComponent&) = 0; 846 847 virtual Component* getParentComponentForMenuOptions (const Options& options) = 0; 848 849 virtual void preparePopupMenuWindow (Component& newWindow) = 0; 850 851 /** Return true if you want your popup menus to scale with the target component's AffineTransform 852 or scale factor 853 */ 854 virtual bool shouldPopupMenuScaleWithTargetComponent (const Options& options) = 0; 855 856 virtual int getPopupMenuBorderSize(); 857 858 virtual int getPopupMenuBorderSizeWithOptions (const Options&) = 0; 859 860 /** Implement this to draw some custom decoration between the columns of the popup menu. 861 862 `getPopupMenuColumnSeparatorWidthWithOptions` must return a positive value in order 863 to display the separator. 864 */ 865 virtual void drawPopupMenuColumnSeparatorWithOptions (Graphics& g, 866 const Rectangle<int>& bounds, 867 const Options&) = 0; 868 869 /** Return the amount of space that should be left between popup menu columns. */ 870 virtual int getPopupMenuColumnSeparatorWidthWithOptions (const Options&) = 0; 871 }; 872 873 private: 874 //============================================================================== 875 JUCE_PUBLIC_IN_DLL_BUILD (struct HelperClasses) 876 class Window; 877 friend struct HelperClasses; 878 friend class MenuBarComponent; 879 880 Array<Item> items; 881 WeakReference<LookAndFeel> lookAndFeel; 882 883 Component* createWindow (const Options&, ApplicationCommandManager**) const; 884 int showWithOptionalCallback (const Options&, ModalComponentManager::Callback*, bool); 885 886 static void setItem (CustomComponent&, const Item*); 887 888 #if JUCE_CATCH_DEPRECATED_CODE_MISUSE 889 // 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 *)890 int drawPopupMenuItem (Graphics&, int, int, bool, bool, bool, bool, bool, const String&, const String&, Image*, const Colour*) { return 0; } 891 #endif 892 893 JUCE_LEAK_DETECTOR (PopupMenu) 894 }; 895 896 } // namespace juce 897