From 0367d5c3a9273efdf4e99a11b4895ea8b2a33b65 Mon Sep 17 00:00:00 2001 From: jules Date: Mon, 17 Jun 2019 17:28:46 +0100 Subject: [PATCH] Added some PopupMenu::addItem overloads which let you attach a lambda callback to be invoked for a menu item. --- .../juce_core/maths/juce_NormalisableRange.h | 2 +- .../messages/juce_MessageManager.cpp | 14 +- .../messages/juce_MessageManager.h | 16 +- .../juce_gui_basics/menus/juce_PopupMenu.cpp | 210 ++++++++++++------ .../juce_gui_basics/menus/juce_PopupMenu.h | 68 +++--- .../juce_gui_basics/widgets/juce_Toolbar.cpp | 2 +- .../misc/juce_KeyMappingEditorComponent.cpp | 40 ++-- 7 files changed, 214 insertions(+), 138 deletions(-) diff --git a/modules/juce_core/maths/juce_NormalisableRange.h b/modules/juce_core/maths/juce_NormalisableRange.h index e18537d11a..0dbcd5c218 100644 --- a/modules/juce_core/maths/juce_NormalisableRange.h +++ b/modules/juce_core/maths/juce_NormalisableRange.h @@ -195,7 +195,7 @@ public: } /** Takes a non-normalised value and snaps it based on either the interval property of - this NormalisedRange or the lambda function supplied to the constructor. + this NormalisableRange or the lambda function supplied to the constructor. */ ValueType snapToLegalValue (ValueType v) const noexcept { diff --git a/modules/juce_events/messages/juce_MessageManager.cpp b/modules/juce_events/messages/juce_MessageManager.cpp index ee83d2f981..bc0d444499 100644 --- a/modules/juce_events/messages/juce_MessageManager.cpp +++ b/modules/juce_events/messages/juce_MessageManager.cpp @@ -164,7 +164,7 @@ private: JUCE_DECLARE_NON_COPYABLE (AsyncFunctionCallback) }; -void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* const func, void* const parameter) +void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* func, void* parameter) { if (isThisTheMessageThread()) return func (parameter); @@ -184,6 +184,18 @@ void* MessageManager::callFunctionOnMessageThread (MessageCallbackFunction* cons return nullptr; } +void MessageManager::callAsync (std::function fn) +{ + struct AsyncCallInvoker : public MessageBase + { + AsyncCallInvoker (std::function f) : callback (std::move (f)) { post(); } + void messageCallback() override { callback(); } + std::function callback; + }; + + new AsyncCallInvoker (std::move (fn)); +} + //============================================================================== void MessageManager::deliverBroadcastMessage (const String& value) { diff --git a/modules/juce_events/messages/juce_MessageManager.h b/modules/juce_events/messages/juce_MessageManager.h index 9b81bf3ee7..b90513212a 100644 --- a/modules/juce_events/messages/juce_MessageManager.h +++ b/modules/juce_events/messages/juce_MessageManager.h @@ -95,11 +95,7 @@ public: //============================================================================== /** Asynchronously invokes a function or C++11 lambda on the message thread. */ - template - static void callAsync (FunctionType&& functionToCall) - { - new AsyncCallInvoker (std::forward (functionToCall)); - } + static void callAsync (std::function functionToCall); /** Calls a function using the message-thread. @@ -340,16 +336,6 @@ private: static void doPlatformSpecificShutdown(); static bool dispatchNextMessageOnSystemQueue (bool returnIfNoPendingMessages); - template - struct AsyncCallInvoker : public MessageBase - { - AsyncCallInvoker (FunctionType&& f) : callback (std::forward (f)) { post(); } - void messageCallback() override { callback(); } - FunctionType callback; - - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AsyncCallInvoker) - }; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MessageManager) }; diff --git a/modules/juce_gui_basics/menus/juce_PopupMenu.cpp b/modules/juce_gui_basics/menus/juce_PopupMenu.cpp index 490acb3ff6..1e24ae6721 100644 --- a/modules/juce_gui_basics/menus/juce_PopupMenu.cpp +++ b/modules/juce_gui_basics/menus/juce_PopupMenu.cpp @@ -223,12 +223,12 @@ struct MenuWindow : public Component setOpaque (lf.findColour (PopupMenu::backgroundColourId).isOpaque() || ! Desktop::canUseSemiTransparentWindows()); - for (int i = 0; i < menu.items.size(); ++i) + for (size_t i = 0; i < menu.items.size(); ++i) { - auto item = menu.items.getUnchecked (i); + auto& item = menu.items[i]; - if (i < menu.items.size() - 1 || ! item->isSeparator) - items.add (new ItemComponent (*item, options.getStandardItemHeight(), *this)); + if (i + 1 < menu.items.size() || ! item.isSeparator) + items.add (new ItemComponent (item, options.getStandardItemHeight(), *this)); } auto targetArea = options.getTargetScreenArea() / scaleFactor; @@ -323,10 +323,15 @@ struct MenuWindow : public Component *managerOfChosenCommand = item->commandManager; } - exitModalState (getResultItemID (item)); + auto resultID = getResultItemID (item); - if (makeInvisible && (deletionChecker != nullptr)) + exitModalState (resultID); + + if (makeInvisible && deletionChecker != nullptr) setVisible (false); + + if (resultID != 0 && item != nullptr && item->action != nullptr) + MessageManager::callAsync (item->action); } } @@ -1262,42 +1267,36 @@ PopupMenu::PopupMenu() } PopupMenu::PopupMenu (const PopupMenu& other) - : lookAndFeel (other.lookAndFeel) + : items (other.items), + lookAndFeel (other.lookAndFeel) { - items.addCopiesOf (other.items); } PopupMenu& PopupMenu::operator= (const PopupMenu& other) { if (this != &other) { + items = other.items; lookAndFeel = other.lookAndFeel; - - clear(); - items.addCopiesOf (other.items); } return *this; } PopupMenu::PopupMenu (PopupMenu&& other) noexcept - : lookAndFeel (other.lookAndFeel) + : items (std::move (other.items)), + lookAndFeel (std::move (other.lookAndFeel)) { - items.swapWith (other.items); } PopupMenu& PopupMenu::operator= (PopupMenu&& other) noexcept { - jassert (this != &other); // hopefully the compiler should make this situation impossible! - - items.swapWith (other.items); + items = std::move (other.items); lookAndFeel = other.lookAndFeel; return *this; } -PopupMenu::~PopupMenu() -{ -} +PopupMenu::~PopupMenu() = default; void PopupMenu::clear() { @@ -1305,13 +1304,54 @@ void PopupMenu::clear() } //============================================================================== -PopupMenu::Item::Item() noexcept +PopupMenu::Item::Item() = default; + +#if JUCE_MSVC && _MSC_VER < 1900 // tedious VC2013 workaround +PopupMenu::Item::Item (Item&& other) + : text (std::move (other.text)), + itemID (other.itemID), + action (std::move (other.action)), + subMenu (std::move (other.subMenu)), + image (std::move (other.image)), + customComponent (std::move (other.customComponent)), + customCallback (std::move (other.customCallback)), + commandManager (other.commandManager), + shortcutKeyDescription (std::move (other.shortcutKeyDescription)), + colour (other.colour), + isEnabled (other.isEnabled), + isTicked (other.isTicked), + isSeparator (other.isSeparator), + isSectionHeader (other.isSectionHeader) { } +PopupMenu::Item& PopupMenu::Item::operator= (Item&& other) +{ + text = std::move (other.text); + itemID = other.itemID; + action = std::move (other.action); + subMenu = std::move (other.subMenu); + image = std::move (other.image); + customComponent = std::move (other.customComponent); + customCallback = std::move (other.customCallback); + commandManager = other.commandManager; + shortcutKeyDescription = std::move (other.shortcutKeyDescription); + colour = other.colour; + isEnabled = other.isEnabled; + isTicked = other.isTicked; + isSeparator = other.isSeparator; + isSectionHeader = other.isSectionHeader; + return *this; +} +#else +PopupMenu::Item::Item (Item&&) = default; +PopupMenu::Item& PopupMenu::Item::operator= (Item&&) = default; +#endif + PopupMenu::Item::Item (const Item& other) : text (other.text), itemID (other.itemID), + action (other.action), subMenu (createCopyIfNotNull (other.subMenu.get())), image (other.image != nullptr ? other.image->createCopy() : nullptr), customComponent (other.customComponent), @@ -1330,6 +1370,7 @@ PopupMenu::Item& PopupMenu::Item::operator= (const Item& other) { text = other.text; itemID = other.itemID; + action = other.action; subMenu.reset (createCopyIfNotNull (other.subMenu.get())); image = other.image != nullptr ? other.image->createCopy() : std::unique_ptr(); customComponent = other.customComponent; @@ -1344,7 +1385,7 @@ PopupMenu::Item& PopupMenu::Item::operator= (const Item& other) return *this; } -void PopupMenu::addItem (const Item& newItem) +void PopupMenu::addItem (Item newItem) { // An ID of 0 is used as a return value to indicate that the user // didn't pick anything, so you shouldn't use it as the ID for an item.. @@ -1352,17 +1393,33 @@ void PopupMenu::addItem (const Item& newItem) || newItem.isSeparator || newItem.isSectionHeader || newItem.subMenu != nullptr); - items.add (new Item (newItem)); + items.push_back (std::move (newItem)); +} + +void PopupMenu::addItem (String itemText, std::function action) +{ + addItem (std::move (itemText), true, false, std::move (action)); } -void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, bool isTicked) +void PopupMenu::addItem (String itemText, bool isActive, bool isTicked, std::function action) { Item i; - i.text = itemText; + i.text = std::move (itemText); + i.action = std::move (action); + i.itemID = -1; + i.isEnabled = isActive; + i.isTicked = isTicked; + addItem (std::move (i)); +} + +void PopupMenu::addItem (int itemResultID, String itemText, bool isActive, bool isTicked) +{ + Item i; + i.text = std::move (itemText); i.itemID = itemResultID; i.isEnabled = isActive; i.isTicked = isTicked; - addItem (i); + addItem (std::move (i)); } static std::unique_ptr createDrawableFromImage (const Image& im) @@ -1377,26 +1434,26 @@ static std::unique_ptr createDrawableFromImage (const Image& im) return {}; } -void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, bool isTicked, const Image& iconToUse) +void PopupMenu::addItem (int itemResultID, String itemText, bool isActive, bool isTicked, const Image& iconToUse) { - addItem (itemResultID, itemText, isActive, isTicked, createDrawableFromImage (iconToUse)); + addItem (itemResultID, std::move (itemText), isActive, isTicked, createDrawableFromImage (iconToUse)); } -void PopupMenu::addItem (int itemResultID, const String& itemText, bool isActive, +void PopupMenu::addItem (int itemResultID, String itemText, bool isActive, bool isTicked, std::unique_ptr iconToUse) { Item i; - i.text = itemText; + i.text = std::move (itemText); i.itemID = itemResultID; i.isEnabled = isActive; i.isTicked = isTicked; i.image = std::move (iconToUse); - addItem (i); + addItem (std::move (i)); } void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager, const CommandID commandID, - const String& displayName, + String displayName, std::unique_ptr iconToUse) { jassert (commandManager != nullptr && commandID != 0); @@ -1407,40 +1464,40 @@ void PopupMenu::addCommandItem (ApplicationCommandManager* commandManager, auto* target = commandManager->getTargetForCommand (commandID, info); Item i; - i.text = displayName.isNotEmpty() ? displayName : info.shortName; + i.text = displayName.isNotEmpty() ? std::move (displayName) : info.shortName; i.itemID = (int) commandID; i.commandManager = commandManager; i.isEnabled = target != nullptr && (info.flags & ApplicationCommandInfo::isDisabled) == 0; i.isTicked = (info.flags & ApplicationCommandInfo::isTicked) != 0; i.image = std::move (iconToUse); - addItem (i); + addItem (std::move (i)); } } -void PopupMenu::addColouredItem (int itemResultID, const String& itemText, Colour itemTextColour, +void PopupMenu::addColouredItem (int itemResultID, String itemText, Colour itemTextColour, bool isActive, bool isTicked, std::unique_ptr iconToUse) { Item i; - i.text = itemText; + i.text = std::move (itemText); i.itemID = itemResultID; i.colour = itemTextColour; i.isEnabled = isActive; i.isTicked = isTicked; i.image = std::move (iconToUse); - addItem (i); + addItem (std::move (i)); } -void PopupMenu::addColouredItem (int itemResultID, const String& itemText, Colour itemTextColour, +void PopupMenu::addColouredItem (int itemResultID, String itemText, Colour itemTextColour, bool isActive, bool isTicked, const Image& iconToUse) { Item i; - i.text = itemText; + i.text = std::move (itemText); i.itemID = itemResultID; i.colour = itemTextColour; i.isEnabled = isActive; i.isTicked = isTicked; i.image = createDrawableFromImage (iconToUse); - addItem (i); + addItem (std::move (i)); } void PopupMenu::addCustomItem (int itemResultID, CustomComponent* cc, const PopupMenu* subMenu) @@ -1449,7 +1506,7 @@ void PopupMenu::addCustomItem (int itemResultID, CustomComponent* cc, const Popu i.itemID = itemResultID; i.customComponent = cc; i.subMenu.reset (createCopyIfNotNull (subMenu)); - addItem (i); + addItem (std::move (i)); } void PopupMenu::addCustomItem (int itemResultID, Component* customComponent, int idealWidth, int idealHeight, @@ -1461,46 +1518,47 @@ void PopupMenu::addCustomItem (int itemResultID, Component* customComponent, int subMenu); } -void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive) +void PopupMenu::addSubMenu (String subMenuName, PopupMenu subMenu, bool isActive) { - addSubMenu (subMenuName, subMenu, isActive, nullptr, false, 0); + addSubMenu (std::move (subMenuName), std::move (subMenu), isActive, nullptr, false, 0); } -void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive, +void PopupMenu::addSubMenu (String subMenuName, PopupMenu subMenu, bool isActive, const Image& iconToUse, bool isTicked, int itemResultID) { - addSubMenu (subMenuName, subMenu, isActive, createDrawableFromImage (iconToUse), isTicked, itemResultID); + addSubMenu (std::move (subMenuName), std::move (subMenu), isActive, + createDrawableFromImage (iconToUse), isTicked, itemResultID); } -void PopupMenu::addSubMenu (const String& subMenuName, const PopupMenu& subMenu, bool isActive, +void PopupMenu::addSubMenu (String subMenuName, PopupMenu subMenu, bool isActive, std::unique_ptr iconToUse, bool isTicked, int itemResultID) { Item i; - i.text = subMenuName; + i.text = std::move (subMenuName); i.itemID = itemResultID; - i.subMenu.reset (new PopupMenu (subMenu)); i.isEnabled = isActive && (itemResultID != 0 || subMenu.getNumItems() > 0); + i.subMenu.reset (new PopupMenu (std::move (subMenu))); i.isTicked = isTicked; i.image = std::move (iconToUse); - addItem (i); + addItem (std::move (i)); } void PopupMenu::addSeparator() { - if (items.size() > 0 && ! items.getLast()->isSeparator) + if (items.size() > 0 && ! items.back().isSeparator) { Item i; i.isSeparator = true; - addItem (i); + addItem (std::move (i)); } } -void PopupMenu::addSectionHeader (const String& title) +void PopupMenu::addSectionHeader (String title) { Item i; - i.text = title; + i.text = std::move (title); i.isSectionHeader = true; - addItem (i); + addItem (std::move (i)); } //============================================================================== @@ -1579,11 +1637,11 @@ PopupMenu::Options PopupMenu::Options::withPreferredPopupDirection (PopupDirecti Component* PopupMenu::createWindow (const Options& options, ApplicationCommandManager** managerOfChosenCommand) const { - return items.isEmpty() ? nullptr - : new HelperClasses::MenuWindow (*this, nullptr, options, - ! options.getTargetScreenArea().isEmpty(), - ModifierKeys::currentModifiers.isAnyMouseButtonDown(), - managerOfChosenCommand); + return items.empty() ? nullptr + : new HelperClasses::MenuWindow (*this, nullptr, options, + ! options.getTargetScreenArea().isEmpty(), + ModifierKeys::currentModifiers.isAnyMouseButtonDown(), + managerOfChosenCommand); } //============================================================================== @@ -1627,8 +1685,9 @@ struct PopupMenuCompletionCallback : public ModalComponentManager::Callback JUCE_DECLARE_NON_COPYABLE (PopupMenuCompletionCallback) }; -int PopupMenu::showWithOptionalCallback (const Options& options, ModalComponentManager::Callback* const userCallback, - const bool canBeModal) +int PopupMenu::showWithOptionalCallback (const Options& options, + ModalComponentManager::Callback* userCallback, + bool canBeModal) { std::unique_ptr userCallbackDeleter (userCallback); std::unique_ptr callback (new PopupMenuCompletionCallback()); @@ -1664,6 +1723,11 @@ int PopupMenu::showMenu (const Options& options) } #endif +void PopupMenu::showMenuAsync (const Options& options) +{ + showWithOptionalCallback (options, nullptr, false); +} + void PopupMenu::showMenuAsync (const Options& options, ModalComponentManager::Callback* userCallback) { #if ! JUCE_MODAL_LOOPS_PERMITTED @@ -1743,8 +1807,8 @@ int PopupMenu::getNumItems() const noexcept { int num = 0; - for (auto* mi : items) - if (! mi->isSeparator) + for (auto& mi : items) + if (! mi.isSeparator) ++num; return num; @@ -1752,9 +1816,9 @@ int PopupMenu::getNumItems() const noexcept bool PopupMenu::containsCommandItem (const int commandID) const { - for (auto* mi : items) - if ((mi->itemID == commandID && mi->commandManager != nullptr) - || (mi->subMenu != nullptr && mi->subMenu->containsCommandItem (commandID))) + for (auto& mi : items) + if ((mi.itemID == commandID && mi.commandManager != nullptr) + || (mi.subMenu != nullptr && mi.subMenu->containsCommandItem (commandID))) return true; return false; @@ -1762,14 +1826,14 @@ bool PopupMenu::containsCommandItem (const int commandID) const bool PopupMenu::containsAnyActiveItems() const noexcept { - for (auto* mi : items) + for (auto& mi : items) { - if (mi->subMenu != nullptr) + if (mi.subMenu != nullptr) { - if (mi->subMenu->containsAnyActiveItems()) + if (mi.subMenu->containsAnyActiveItems()) return true; } - else if (mi->isEnabled) + else if (mi.isEnabled) { return true; } @@ -1832,14 +1896,14 @@ PopupMenu::MenuItemIterator::MenuItemIterator (const PopupMenu& m, bool recurse) menus.add (&m); } -PopupMenu::MenuItemIterator::~MenuItemIterator() {} +PopupMenu::MenuItemIterator::~MenuItemIterator() = default; bool PopupMenu::MenuItemIterator::next() { if (index.size() == 0 || menus.getLast()->items.size() == 0) return false; - currentItem = menus.getLast()->items.getUnchecked (index.getLast()); + currentItem = const_cast (&(menus.getLast()->items[(size_t) index.getLast()])); if (searchRecursively && currentItem->subMenu != nullptr) { @@ -1851,7 +1915,7 @@ bool PopupMenu::MenuItemIterator::next() index.setUnchecked (index.size() - 1, index.getLast() + 1); } - while (index.size() > 0 && index.getLast() >= menus.getLast()->items.size()) + while (index.size() > 0 && index.getLast() >= (int) menus.getLast()->items.size()) { index.removeLast(); menus.removeLast(); @@ -1863,7 +1927,7 @@ bool PopupMenu::MenuItemIterator::next() return true; } -PopupMenu::Item& PopupMenu::MenuItemIterator::getItem() const noexcept +PopupMenu::Item& PopupMenu::MenuItemIterator::getItem() const { jassert (currentItem != nullptr); return *(currentItem); diff --git a/modules/juce_gui_basics/menus/juce_PopupMenu.h b/modules/juce_gui_basics/menus/juce_PopupMenu.h index 8ea675197f..3e94082745 100644 --- a/modules/juce_gui_basics/menus/juce_PopupMenu.h +++ b/modules/juce_gui_basics/menus/juce_PopupMenu.h @@ -79,13 +79,7 @@ namespace juce */ class JUCE_API PopupMenu { -private: - class Window; - public: - class CustomComponent; - class CustomCallback; - //============================================================================== /** Creates an empty popup menu. */ PopupMenu(); @@ -105,6 +99,10 @@ public: /** Move assignment operator */ PopupMenu& operator= (PopupMenu&&) noexcept; + //============================================================================== + class CustomComponent; + class CustomCallback; + //============================================================================== /** Resets the menu, removing all its items. */ void clear(); @@ -116,13 +114,12 @@ public: You'll need to set some fields after creating an Item before you can add it to a PopupMenu */ - Item() noexcept; + Item(); - /** Creates a copy of an item. */ Item (const Item&); - - /** Creates a copy of an item. */ Item& operator= (const Item&); + Item (Item&&); + Item& operator= (Item&&); /** The menu item's name. */ String text; @@ -130,6 +127,9 @@ public: /** The menu item's ID. This must not be 0 if you want the item to be triggerable! */ int itemID = 0; + /** An optional function which should be invoked when this menu item is triggered. */ + std::function action; + /** A sub-menu, or nullptr if there isn't one. */ std::unique_ptr subMenu; @@ -175,7 +175,17 @@ public: You can call this method for full control over the item that is added, or use the other addItem helper methods if you want to pass arguments rather than creating an Item object. */ - void addItem (const Item& newItem); + void addItem (Item newItem); + + /** Adds an item to the menu with an action callback. */ + void addItem (String itemText, + std::function action); + + /** Adds an item to the menu with an action callback. */ + void addItem (String itemText, + bool isEnabled, + bool isTicked, + std::function action); /** Appends a new text item for this menu to show. @@ -190,7 +200,7 @@ public: @see addSeparator, addColouredItem, addCustomItem, addSubMenu */ void addItem (int itemResultID, - const String& itemText, + String itemText, bool isEnabled = true, bool isTicked = false); @@ -208,7 +218,7 @@ public: @see addSeparator, addColouredItem, addCustomItem, addSubMenu */ void addItem (int itemResultID, - const String& itemText, + String itemText, bool isEnabled, bool isTicked, const Image& iconToUse); @@ -228,7 +238,7 @@ public: @see addSeparator, addColouredItem, addCustomItem, addSubMenu */ void addItem (int itemResultID, - const String& itemText, + String itemText, bool isEnabled, bool isTicked, std::unique_ptr iconToUse); @@ -246,7 +256,7 @@ public: */ void addCommandItem (ApplicationCommandManager* commandManager, CommandID commandID, - const String& displayName = String(), + String displayName = {}, std::unique_ptr iconToUse = {}); /** Appends a text item with a special colour. @@ -256,11 +266,11 @@ public: current look-and-feel. See addItem() for a description of the parameters. */ void addColouredItem (int itemResultID, - const String& itemText, + String itemText, Colour itemTextColour, bool isEnabled = true, bool isTicked = false, - const Image& iconToUse = Image()); + const Image& iconToUse = {}); /** Appends a text item with a special colour. @@ -269,7 +279,7 @@ public: current look-and-feel. See addItem() for a description of the parameters. */ void addColouredItem (int itemResultID, - const String& itemText, + String itemText, Colour itemTextColour, bool isEnabled, bool isTicked, @@ -314,8 +324,8 @@ public: If the itemResultID argument is non-zero, then the sub-menu item itself can be clicked to trigger it as a command. */ - void addSubMenu (const String& subMenuName, - const PopupMenu& subMenu, + void addSubMenu (String subMenuName, + PopupMenu subMenu, bool isEnabled = true); /** Appends a sub-menu with an icon. @@ -324,8 +334,8 @@ public: If the itemResultID argument is non-zero, then the sub-menu item itself can be clicked to trigger it as a command. */ - void addSubMenu (const String& subMenuName, - const PopupMenu& subMenu, + void addSubMenu (String subMenuName, + PopupMenu subMenu, bool isEnabled, const Image& iconToUse, bool isTicked = false, @@ -341,8 +351,8 @@ public: the item. The menu will take ownership of this drawable object and will delete it later when no longer needed */ - void addSubMenu (const String& subMenuName, - const PopupMenu& subMenu, + void addSubMenu (String subMenuName, + PopupMenu subMenu, bool isEnabled, std::unique_ptr iconToUse, bool isTicked = false, @@ -360,7 +370,7 @@ public: This is a bold-font items which can be used as a header to separate the items into named groups. */ - void addSectionHeader (const String& title); + void addSectionHeader (String title); /** Returns the number of items that the menu currently contains. (This doesn't count separators). @@ -509,6 +519,9 @@ public: int showMenu (const Options& options); #endif + /** Runs the menu asynchronously. */ + void showMenuAsync (const Options& options); + /** Runs the menu asynchronously, with a user-provided callback that will receive the result. */ void showMenuAsync (const Options& options, ModalComponentManager::Callback* callback); @@ -594,7 +607,7 @@ public: /** Returns a reference to the description of the current item. It is only valid to call this after next() has returned true! */ - Item& getItem() const noexcept; + Item& getItem() const; private: //============================================================================== @@ -749,10 +762,11 @@ public: private: //============================================================================== JUCE_PUBLIC_IN_DLL_BUILD (struct HelperClasses) + class Window; friend struct HelperClasses; friend class MenuBarComponent; - OwnedArray items; + std::vector items; WeakReference lookAndFeel; Component* createWindow (const Options&, ApplicationCommandManager**) const; diff --git a/modules/juce_gui_basics/widgets/juce_Toolbar.cpp b/modules/juce_gui_basics/widgets/juce_Toolbar.cpp index 710c1f75d7..7f24b86bc6 100644 --- a/modules/juce_gui_basics/widgets/juce_Toolbar.cpp +++ b/modules/juce_gui_basics/widgets/juce_Toolbar.cpp @@ -543,7 +543,7 @@ void Toolbar::showMissingItems() { PopupMenu m; m.addCustomItem (1, new MissingItemsComponent (*this, getThickness())); - m.showMenuAsync (PopupMenu::Options().withTargetComponent (missingItemsButton.get()), [] (int) {}); + m.showMenuAsync (PopupMenu::Options().withTargetComponent (missingItemsButton.get())); } } diff --git a/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp b/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp index cb05f6b472..70390eb082 100644 --- a/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp +++ b/modules/juce_gui_extra/misc/juce_KeyMappingEditorComponent.cpp @@ -30,8 +30,8 @@ namespace juce class KeyMappingEditorComponent::ChangeKeyButton : public Button { public: - ChangeKeyButton (KeyMappingEditorComponent& kec, const CommandID command, - const String& keyName, const int keyIndex) + ChangeKeyButton (KeyMappingEditorComponent& kec, CommandID command, + const String& keyName, int keyIndex) : Button (keyName), owner (kec), commandID (command), @@ -50,31 +50,31 @@ public: keyNum >= 0 ? getName() : String()); } - static void menuCallback (int result, ChangeKeyButton* button) - { - if (button != nullptr) - { - switch (result) - { - case 1: button->assignNewKey(); break; - case 2: button->owner.getMappings().removeKeyPress (button->commandID, button->keyNum); break; - default: break; - } - } - } - void clicked() override { if (keyNum >= 0) { - // existing key clicked.. + Component::SafePointer button (this); PopupMenu m; - m.addItem (1, TRANS("Change this key-mapping")); + + m.addItem (TRANS("Change this key-mapping"), + [button] + { + if (button != nullptr) + button.getComponent()->assignNewKey(); + }); + m.addSeparator(); - m.addItem (2, TRANS("Remove this key-mapping")); - m.showMenuAsync (PopupMenu::Options(), - ModalCallbackFunction::forComponent (menuCallback, this)); + m.addItem (TRANS("Remove this key-mapping"), + [button] + { + if (button != nullptr) + button->owner.getMappings().removeKeyPress (button->commandID, + button->keyNum); + }); + + m.showMenuAsync (PopupMenu::Options().withTargetComponent (this)); } else {