If a TextEdit is in progress with a right click PopupMenu visible and someone exits the application this causes an access violation and crash.
I first found the bug by having a right click menu on my plugin. As a workaround I tried adding the menu to it’s own window via the addToDesktop method but the menu still remains visible and fails to receive the focusLost virtual method when it has lost focus by someone clicking on the host.
It’s the toughest problem I’ve had with plugins so far. I think the only workaround might be some kind of special bodge that uses a normal OS modal state to block the rest of the app from getting events while the menu is there, but I tried that a while ago and hit all sorts of other stupid problems…
Below is an example usage of the DesktopPopupMenu, then menu removes itself so you just have to match whatever you called the menu in your callback, that way you can have multiple different menus doing different sets of commands through the same interface and not worry about trying to keeps the ids different:
void TheGlueEditorComponent::PopupPresetMenu ()
{
DesktopPopupMenu* menu = new DesktopPopupMenu (T("PresetMenu"), this);
menu->addItem (Menu_Rename, T("Rename"), true);
menu->addSeparator ();
menu->addItem (Menu_Load, T("Load..."), true);
menu->addItem (Menu_Save, T("Save..."), true);
menu->addSeparator ();
menu->addItem (Menu_Copy, T("Copy"), true);
menu->addItem (Menu_Paste, T("Paste"), true);
if (buttonab && buttonab->getToggleState ())
{
menu->addItem (Menu_Duplicate, T("Copy B->A"), true);
}
else
{
menu->addItem (Menu_Duplicate, T("Copy A->B"), true);
}
menu->addSeparator ();
menu->addItem (Menu_About, T("About..."), true);
menu->showAt (buttonpreset);
}
void TheGlueEditorComponent::menuAccepted (DesktopPopupMenu* menu, const int item)
{
if (menu->getName ().equalsIgnoreCase (T("PresetMenu")))
{
switch (item)
{
case Menu_None : break;
case Menu_Rename : CommandRename (); break;
case Menu_Load : CommandLoad (); break;
case Menu_Save : CommandSave (); break;
case Menu_Copy : CommandCopy (); break;
case Menu_Paste : CommandPaste (); break;
case Menu_Duplicate: CommandDuplicate (); break;
case Menu_About : CommandAbout (); break;
}
}
}
void TheGlueEditorComponent::menuRejected (DesktopPopupMenu* menu)
{
// do nothing
}
Nice, but this is only a kind of workaround for a problem that is not specific to PopupMenu’s only. When there’s a modal dialog opened, the same problem arises. So one would also need to apply it to that.
Revisiting this issue because 10 years later, I still see this in our JUCE plugins. If the user opens a popup menu, then closes the project/plugin, the DAW crashes.
Is there any way to get around this, other than what is described here? (I would think that the owner of the menu could close it somehow if it is open when the class is being destroyed, but I tried something like that with VSTGUI for open dialogs, and didn’t have much luck.)
@HowardAntares in our plugins we call the static method PopupMenu::dismissAllActiveMenus() in the destructor of our AudioProcessorEditor. Does this work for you?
I’ll see if I can step through in the debugger and get any more, but given that the component that opened the menu has been destroyed, I’m not hopeful.
EDIT: Looks like the added call to setStateAsDirty() is causing this problem. I added that to make sure that DAWs know something might have changed in the plugin when changing menu selections, so that it will prompt to save upon closing. Maybe I should only do that if one of the valid selections was made?
You’re not calling the show() method, are you?
The only safe way to show a popup menu in a plugin is with showMenuAsync()
If you want to be sure, just set JUCE_MODAL_LOOPS_PERMITTED=0 in your project, and it should be impossible to call anything that causes these shutdown modal loop problems.
Yes, I am calling show(). I had no idea that this was a no-no. We stopped using modal dialogs, but I had not been aware that a menu could cause similar problems. Using dismissAllActiveMenus() in my editor’s destructor, and not calling any other code if I don’t get one of my expected responses back, seems to work, though. Should I rewrite my menu using the async method instead?
[EDIT] Changed my code to use showMenuAsync(), with a lambda for the callback, allowing my code to have minimal changes. Seems to work perfectly. Thanks!