Best way to show a plugin editor in a host application?

Hi there,

I’m working on a audio application written in C++ (or Qt to be more specific), and recently I started working on VST/AU plugin support. I intend to use JUCE to scan the available plugins, show the plugin editor, as well as process audio for the plugins.

So far I was able to find plugins in the users systems, create plugin instances for them, and show their editors.

A bit more details about how I’m currently showing the editor:

  • Creating a AudioPluginInstance using a PluginDescription
  • Then creating a AudioProcessorEditor using AudioPluginInstance::createEditorIfNeeded()
  • Then show the editor using AudioProcessorEditor::addToDesktop()

It seems to work well for both VST3 and AU plugins so far.

However, if I enable the “close” button for the plugin editor window (i.e. via ComponentPeer::StyleFlags::windowHasCloseButton), even though the “close” button is shown on the title bar of the editor window, it doesn’t do any thing (e.g. it doesn’t actually close the editor window when clicked). I’m also hitting an assertion failing at juce_Component.cpp:821:

void Component::userTriedToCloseWindow()
{
    /* This means that the user's trying to get rid of your window with the 'close window' system
       menu option (on windows) or possibly the task manager - you should really handle this
       and delete or hide your component in an appropriate way.

       If you want to ignore the event and don't want to trigger this assertion, just override
       this method and do nothing.
    */
    jassertfalse;
}

I’m looking for advice on the best way to handle the “close” button of an editor window in a host application.

Some options I can think of:

  • Manage the editor window in my own code, and pass the window handle when calling AudioProcessorEditor::addToDesktop()
    • I think this might be the way to go, but this may involve extra work like properly resizing the editor window before showing it or when the plugin editor changes size
  • Somehow “inject” code into the chain of functions that eventually calls userTriedToCloseWindow(), and perform the window close
    • This feels a bit hacky, but I think this might be the only thing needed in my particular case
    • I tried to find an existing overriding point for this (e.g. ComponentListener), but couldn’t find any

Cheers.

I started to work on the Windows side as well and have noticed something else:

  • When using the approach described in the original post to display the editor window, the resulting editor window doesn’t have a title bar (even though the ComponentPeer::StyleFlags::windowHasTitleBar flag was passed in when calling AudioProcessorEditor::addToDesktop())

This makes me think maybe managing my own window then display the editor inside the managed window is the way forward…

I tried to manage my own window and send the platform specific raw window handle to AudioProcessorEditor::addToDesktop(). It seems to work well on Windows, will see how it work on macOS…

2 Likes

So on macOS it’s a bit trickier than I thought.

When I used the values returned from AudioProcessorEditor::getWidth()/getHeight() to set the raw window’s size, it only worked for some plugins. Digging in a bit further I noticed that for some plugins, these values may be 1 (which could explain why the window was not visible for some).

I don’t have any clues at the moment, but since AudioProcessorEditor::addToDesktop() worked when no raw window handle was provided, there must be a way to properly fetch the editor’s initial size (I may need to dig into JUCE source code).

Also, an extra note about the raw window handle on macOS:

  • It seems addToDesktop() actually expects a NSView* (instead of a NSWindow*)
  • Luckily for my case, the raw window handle I get (from QWindow) is also a NSView*

After digging into JUCE source code, it seems for some plugins, the editor size is fetched asynchronously - meaning when the AudioProcessorEditor was initially created, the size was set to a placeholder value (e.g. 1), and then the correctly size will be updated sometime later, and we can listen for this via ComponentListener::componentMovedOrResized().

After I listened for that callback and fetch the editor’s size again, I was able to show the editor in my own window with the correct size.

Almost all of these questions can be answered by looking at the code for the JUCE AudioPluginHost (in the extras folder). For example, specifically, PluginWindow.h shows how to handle a plugin’s window and how to close it.

Thanks, I will take a look!