Open a Window in JUCE and put a PluginEditor into it

The tl;dr How do I open a window in Juce to display a plugin Editor?

I am following the Cascading plug-in effects tutorial at JUCE: Tutorial: Cascading plug-in effects which shows how to dynamically load AudioProcessor objects.

I would like to open their GUIs as they’re loaded. This could be in separate windows or in the window that’s got the slots for the graph.

I’ve also used ProJucer to create a new plugin. I’ve put the new plugin’s files into the tutorial’s files and used ProJucer to add PluginEditor.cpp and PluginEditor.h to the tutorial project.

I’ve changed the PluginEditor.h so the private class member audioProcessor is of type ProcessorBase&. And made ProcessorBase& the expected type for the data passed to the constructor.

In the tutorial code, in the header file, I’ve changed the ProcessorBase class so that the methods hasEditor() and createEditor() are just declarations and then modified main.cpp as follows:

#include "PluginEditor.h"
#include <iostream>

//=======================================================
juce::AudioProcessorEditor* ProcessorBase::createEditor()           
{ 
  std::cout "asking for an editor\n";
  return  new NewProjectAudioProcessorEditor (*this); 
}
bool ProcessorBase::hasEditor() const                               
{ return true; }

In the included header, in the TutorialProcessor class, in the updateGraph() method, under the if (hasChanged), I’ve added slot->getProcessor()->createEditorIfNeeded (); on line 387-ish.

I can tell from my cout that createEditor() is being called and know that the constructor for the editor is running, but no window opens.

How do I tell it to open a window?

The code compiles and runs, but no window opens.

If you are creating a plugin, you should be using AudioProcessorEditor as the base class.

Before you do cascading effects, you should familiarize yourself with these tutorials:

https://docs.juce.com/master/tutorial_create_projucer_basic_plugin.html

https://docs.juce.com/master/tutorial_code_basic_plugin.html

If the AudioProcessors you are playing are compiled into the project, as seen in this tutorial, you can add them as child using addAndMakeVisible and set the bounds. However the AudioProcessorEditor may change it’s size by itself, so the usual resized() routine might be different, I haven’t tried.

If it is in a window, it is best to use the ResizeableWindow, which has a bool flag when setting the AudioProcessorEditor as content:

void setContentOwned (Component *newContentComponent, bool resizeToFitWhenContentChangesSize)

If you show plugin editors, that are not compiled in, the AudioProcessorEditor will take care of wrapping the editor into a HWNDComponent, NSViewComponent or XEmbedComponent.

In the updateGraph() method I’ve added two lines:

 ResizableWindow win = ResizableWindow (slot->getProcessor()->getName(), true);
 win.setContentOwned (slot->getProcessor()->createEditorIfNeeded (), true);

Which generates an error

In file included from ../../Source/Main.cpp:10:
../../Source/AudioProcessorGraphTutorial_01.h: In member function ‘void TutorialProcessor::updateGraph()’:
../../Source/AudioProcessorGraphTutorial_01.h:388:97: error: use of deleted function ‘juce::ResizableWindow::ResizableWindow(const juce::ResizableWindow&)’
  388 | leWindow win = ResizableWindow (slot->getProcessor()->getName(), true);
      |                                                                      ^

In file included from /home/celesteh/Documents/code/JUCE/modules/juce_core/system/juce_StandardHeader.h:79,
                 from /home/celesteh/Documents/code/JUCE/modules/juce_core/juce_core.h:204,
                 from /home/celesteh/Documents/code/JUCE/modules/juce_audio_basics/juce_audio_basics.h:54,
                 from ../../JuceLibraryCode/JuceHeader.h:16,
                 from ../../Source/Main.cpp:9:
/home/celesteh/Documents/code/JUCE/modules/juce_gui_basics/windows/juce_ResizableWindow.h:410:51: note: declared here
  410 |     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableWindow)
      |                                                   ^~~~~~~~~~~~~~~
/home/celesteh/Documents/code/JUCE/modules/juce_core/system/juce_PlatformDefs.h:231:5: note: in definition of macro ‘JUCE_DECLARE_NON_COPYABLE’
  231 |     className (const className&) = delete;\
      |     ^~~~~~~~~
/home/celesteh/Documents/code/JUCE/modules/juce_gui_basics/windows/juce_ResizableWindow.h:410:5: note: in expansion of macro ‘JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR’
  410 |     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ResizableWindow)
      |     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
make: *** [Makefile:181: build/intermediate/Debug/Main_90ebc5c2.o] Error 1

Yes, this cannot work.
You are trying to create a ResizableWindow on the stack. But it would be deleted when the scope ends.

Instead you need a unique_ptr owned somewhere as member of a class like this:

// member
std::unique_ptr<juce::ResizableWindow> win;

// created like this:
win = std::make_unique<juce::ResizableWindow>(slot->getProcessor()->getName(), true);
win->setContentOwned (slot->getProcessor()->createEditor(), true);
win->addToDesktop (/* flags */);
win->setVisible (true);

Notes:
Don’t use createEditorIfNeeded(), because you take ownership. The IfNeeded might return a pointer to an editor instance, that is already owned somewhere else.

And I am not sure, if updateGraph() is a great place, it sounds it could be called much more often than you think.

You’ve been extremely helpful and I think I just have one last question about the flags and how they work:

                    int flags = 
                        static_cast<int>(ComponentPeer::StyleFlags::windowHasTitleBar) | 
                        static_cast<int>(ComponentPeer::StyleFlags::windowIsResizable);
                  
                    processor->win->addToDesktop(flags );

This opens a window with no titlebar that I can’t move or resize.