The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

840 lines
38KB

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