Detecting Audio Plugin Editor Window Movement


#1

Hello Everyone,

I am trying to detect movement of the audio plugin editor window, but without much success. I am hoping that someone can give me a hint, or confirm there is an issue in the framework.

I am on JUCE v4.3.1 at the moment, but I would convince my people to upgrade to latest if that would solve this problem. Currently working on macOS Sierra 10.12.4 and need for this to work eventually on Win32.

As I said, I need to detect when the audio plugin editor is moved by the user. (The reason is that we are integrating a chromium embedded framework browser window for the plugin editor, and as was pointed out in the docs for ComponentMovementWatcher (https://www.juce.com/doc/classComponentMovementWatcher#details), I need to keep my custom window in the right place, just like JUCE’s own WebBrowserComponent.)

Besides ComponentMovementWatcher docs, I have also seen this post (ComponentMovementWatcher class is not working as expected), where near the end of the discussion, Jules says “The only thing it [the ComponentMovementWatcher] watches is the target component, and it will only tell you when that component is moved relative to either the screen (if it is itself a window) or the top-level comp, if it’s embedded at some arbitrary depth within a window.” I want to know when the window itself moves relative to the screen. Simple enough.

I realized after some experimentation that you can’t just create the ComponentMovementWatcher too early, because the hierarchy of Components might not be fully built, and the ultimate parent target Component (the window) of the ComponentMovementWatcher might not be in the Component hierarchy yet (to pass into the ComponentMovementWatcher(Component *component) constructor). My solution was to make my browser component a ComponentListener, and create my ComponentMovementWatcher in componentParentHierarchyChanged(). The last time the function is called, everything ought to be built, and the call to browserComponent.getTopLevelComponent() should return the top level window:

class TopWindowMovementWatcher;

class WebBrowserComponent :  public Component, public ComponentListener
{
    . . .
    void componentParentHierarchyChanged( Component & component )
    {
        topWindowMovementWatcher = new TopWindowMovementWatcher( *this );
    }
    
    ScopedPointer<TopWindowMovementWatcher> topWindowMovementWatcher;
};

class TopWindowMovementWatcher : public ComponentMovementWatcher
{
    TopWindowMovementWatcher( WebBrowserComponent& browserComponent )
        : ComponentMovementWatcher( browserComponent.getTopLevelComponent() ),
          webBrowserComponent( browserComponent )
    {
    }

    . . .
    // From ComponentMovementWatcher
    void componentMovedOrResized( bool wasMoved, bool wasResized ) override;
    {
        Rectangle<int> bounds( webBrowserComponent.getScreenBounds() );
        . . .
        std::cerr << "typeid(*getComponent()).name(): " << typeid(*getComponent()).name() << std::endl;
    }

    WebBrowserComponent & webBrowserComponent;
};

The problem is that the componentMovedOrResized() function gets called only a few times at the time the plugin editor is created, and never again even when I move the editor on screen. Note that the top level window just doesn’t ever seem to appear, as my diagnostic messages show:

. . .
typeid(*getComponent()).name(): 15MyMainComponent
typeid(*getComponent()).name(): 28MypluginAudioProcessorEditor
typeid(*getComponent()).name(): N6JuceAU16EditorCompHolderE
typeid(*getComponent()).name(): N6JuceAU16EditorCompHolderE
typeid(*getComponent()).name(): N6JuceAU16EditorCompHolderE
typeid(*getComponent()).name(): N6JuceAU16EditorCompHolderE

A search in the JUCE codebase shows that the internal class EditorCompHolder is a Component and not any sort of window:

class EditorCompHolder  : public Component
{. . .}

And the EditorCompHolder is added to the desktop in the same JUCE source file:

static NSView* createViewFor (AudioProcessor* filter, JuceAU* au, AudioProcessorEditor* const editor)
{
    EditorCompHolder* editorCompHolder = new EditorCompHolder (editor);
    . . .
    editorCompHolder->addToDesktop (0, (void*) view);
    . . .
}

(These last lines I don’t completely understand.)

But in the end, the EditorCompHolder never moves relative to its parent (something on the “desktop”, I guess), and I don’t get componentMovedOrResized() messages.

So do I have a fundamental misunderstanding of how this is supposed to work: you register a ComponentMovementWatcher to listen for movement of its target relative to the screen (if the target is a window) or the top level Component (if the target is not a window) by passing that target to the ComponentMovementWatcher constructor? Or is there something not quite correct in the JUCE implementation?

Thanks for any help,

Isaac


#2

Full disclosure: I haven’t read your post thoroughly, but the plug-in window is owned by the host… so you wouldn’t get a notification if it’s moved.

Rail


#3

Hi Rail,

That would explain it.

My work around at this point is to set up a Thread that periodically checks if the position of the editor window has changed, and if it has, move my browser window to the correct location.

I already have a loop like that in order to integrate the event loop of the chromium embedded framework (cef) into the JUCE event loop: never enter the cef loop, just call the cef non-blocking event handling function periodically from the JUCE event Thread. Yes, this is polling, but this is how you have to do it. And for integrating cef, this works pretty good. It doesn’t use up too many machine cycles and it is safe if you properly call std::condition_variable::wait_for() in the loop and adjust the polling frequency carefully.

So I stick another call to do my editor window position change check into this same callback function (for polling cef event handling), and what I get is a not very smooth move: the browser part of the window lags behind the title bar when I drag the title bar. I can’t go with this other than as an experiment. It makes me look like a clown.

Window moves (as well as resizes) are pretty fundamental. Is there any way to get any sort of callback as the plugin editor window is moved? Any info would be appreciated even if it takes separate implementations for macOS and Win32.

Thanks.

Isaac


#4

If there would be a way to get this information (especially while live-moving the window) then JUCE would have a lot less bugs to worry about (see this thread for example). I don’t think there is an easy cross-platform way to do this. See this bug for example: