Lookandfeel change when closing plugin


#1

Hi,

So I am in the process of creating an AU/VST plugin. I have overrided the lookandfeel class in the plugin editor.

In the editor constructor I have:

LookAndFeel::setDefaultLookAndFeel (&myLookAndFeelClass);

this works fine.

However, if I open two instances of the plugin and close one, on the close, the lookandfeel of the second plugin reverts back to the original and reverts to the original graphics. I am sure this can be fixed by calling setdefaultlookandfeel during the timercallback as I assume the problem is the default lookandfeel is set back to the original on bypassing the plugin. But, calling it every time the timercallback is called seems unnecessary.

I was wondering if anyone has any solutions.

Thank You,
Zack


#2

just put something like that in your AudioProcessorEditor :

struct DefaultLookAndFeel
{
    DefaultLookAndFeel()  { LookAndFeel::setDefaultLookAndFeel (&lnf);    }
    ~DefaultLookAndFeel() { LookAndFeel::setDefaultLookAndFeel (nullptr); }

    YourLookAndFeelClass lnf;
};

SharedResourcePointer<DefaultLookAndFeel> defaultLnf;

#3

…it’s worth making it clear for any beginners reading this that in the code above, the SharedResourcePointer variable is intended to be a member of the plugin editor class, and not a global variable!


#4

Thank You - This seems to work!


#5

Just as I ran into this issue, thanks!


#6

Whats the best way to have the look and feel class created when the editor is first opened, and before anything else is constructed in the editor, but then kept around until the last processor is deleted (it’ll make future re-openings of the editor much faster) ?


#7

Probably the easiest way I can think of is with a singleton, the getInstance() can be called either in the editor ctor or if you really need it earlier than that, in the createEditor() function before the editor is actually created. Using a singleton it will mean it’s only created the once.

    struct DefaultLookAndFeel : public DeletedAtShutdown
    {
        DefaultLookAndFeel() { LookAndFeel::setDefaultLookAndFeel (&lnf); }
        ~MySingleton()
        {
            LookAndFeel::setDefaultLookAndFeel (nullptr);
            clearSingletonInstance();
        }

        YourLookAndFeelClass lnf;
        JUCE_DECLARE_SINGLETON (MySingleton, true)
    };

    // ..and this goes in a suitable .cpp file:
    JUCE_IMPLEMENT_SINGLETON (MySingleton)

Note: the above is untested but I believe it should set your look and feel when you create the singleton (when editor is first opened), and it will remove it while shutting down the plugin.


#8

SharedResourcePointer is probably the best for things like this.


#9

…provided that you store it in the Processor, not in the Editor, even if it is only used by the latter.

If you have it as a member of the Editor, it ends up being deleted and created with it if you have only one around, while @jimc explicitly said:


#10

I could be misunderstanding how the the SharedResourcePointer works but wouldn’t it create the object when the processor is created? @jimc asked for it to be created when the first editor is opened.


#11

Exactly - this is the ideal behaviour. Because you don’t want to incur too much overhead when just creating the Processor - this is why my DAW sessions take ages to load … plugin startup time!

Also - when in a plugin is DeletedAtShutdown actually called - I’m going to have to dig into that!


#12

Yes see shutdownJuce_GUI it’s called by all the wrappers.


#13

Ok - cheers @Anthony_Nicholls ! That pretty much settles it I think, a regular JUCE singleton should do it… it’s a little faffy compared to the SharedResourcePointer but it’ll be easy to get the right behaviour…


#14

Or this variation on SharedResourcePointer that delays the object creation until it’s required.

template <typename SharedObjectType>
class DelayedSharedResourcePointer
{
public:
    DelayedSharedResourcePointer() { initialise(); }
    DelayedSharedResourcePointer (const DelayedSharedResourcePointer&) { initialise(); }

    /** Destructor.  If no other DelayedSharedResourcePointer objects of this type exist, this will also delete the shared object to which it refers.
    */
    ~DelayedSharedResourcePointer()
    {
        auto& holder = getSharedObjectHolder();
        const SpinLock::ScopedLockType sl (holder.lock);

        if (--(holder.refCount) == 0)
            holder.sharedInstance = nullptr;
    }

    /** Returns the shared object, creating it if it doesn't already exist. */
    SharedObjectType& get() const
    {
        auto& holder = getSharedObjectHolder();
        const SpinLock::ScopedLockType sl (holder.lock);

        if (holder.sharedInstance == nullptr)
            holder.sharedInstance = new SharedObjectType();

        return *holder.sharedInstance;
    }

    /** Returns the number of SharedResourcePointers that are currently holding the shared object. */
    int getReferenceCount() const noexcept              { return getSharedObjectHolder().refCount; }

private:
    struct SharedObjectHolder
    {
        SpinLock lock;
        ScopedPointer<SharedObjectType> sharedInstance;
        int refCount;
    };

    static SharedObjectHolder& getSharedObjectHolder() noexcept
    {
        static void* holder [(sizeof (SharedObjectHolder) + sizeof(void*) - 1) / sizeof(void*)] = { nullptr };
        return *reinterpret_cast<SharedObjectHolder*> (holder);
    }

    void initialise()
    {
        auto& holder = getSharedObjectHolder();
        const SpinLock::ScopedLockType sl (holder.lock);

        ++holder.refCount;
    }

    // There's no need to assign to a SharedResourcePointer because every
    // instance of the class is exactly the same!
    DelayedSharedResourcePointer& operator= (const DelayedSharedResourcePointer&) = delete;

    JUCE_LEAK_DETECTOR (DelayedSharedResourcePointer)
};

#15

As yet untested code by the way :slight_smile: Feel free to pick holes in it.


#16

Would it work to add a ScopedPointer or std::unique_ptr to a SharedResourcePointer in the processor, have it nullptr by default, then call new SharedResourcePointer<MySharedResourceType>() in createEditor() or the editor constructor?

Alternatively maybe a bool template argument added to SharedResourcePointer that could have the SharedResource be created in get() rather than in the constructor (which is what I assume you have done in the DelayedSharedResourcePointer above).


#17

I started doing something like your first suggestion - but it was going to be difficult to follow when I come back to it next year.

So the second option is pretty sound - but the way SharedResourcePointer is set up made me think it was easier to have a brand new class. Just testing it now …


#18

The ScopedPointer to a SharedResourcePointer could be hidden away in the DelayedSharedResourcePointer class something like…

template<typename SharedObjectType>
class DelayedSharedResourcePointer
{
public:
    DelayedSharedResourcePointer() {}
    DelayedSharedResourcePointer (const DelayedSharedResourcePointer& other)
    {
        sharedResource = other.sharedResource;
    }
    
    DelayedSharedResourcePointer& operator= (const DelayedSharedResourcePointer& other)
    {
        sharedResource = other.sharedResource;
    }
    
    /** Destructor.
     If no other SharedResourcePointer objects exist, this will also delete
     the shared object to which it refers.
     */
    ~DelayedSharedResourcePointer() {}
    
    /** Returns the shared object. */
    operator SharedObjectType*() const noexcept
    {
        createIfNeeded();
        return sharedResource->operator*();
    }
    
    /** Returns the shared object. */
    SharedObjectType& get() const noexcept
    {
        createIfNeeded();
        return sharedResource->get();
    }
    
    /** Returns the object that this pointer references.
     The pointer returned may be a nullptr, of course.
     */
    SharedObjectType& getObject() const noexcept
    {
        createIfNeeded();
        return sharedResource->getObject();
    }
    
    /** Returns the shared object. */
    SharedObjectType* operator->() const noexcept
    {
        createIfNeeded();
        return sharedResource->operator->();
    }
    
    /** Returns the number of SharedResourcePointers that are currently holding the shared object. */
    int getReferenceCount() const noexcept
    {
        if (sharedResource == nullptr)
            return 0;
        
        return sharedResource->getReferenceCount();
    }
private:
    void createIfNeeded()
    {
        if (sharedResource == nullptr)
            sharedResource = new SharedResourcePointer<SharedObjectType>();
    }
    
    ScopedPointer<SharedResourcePointer<SharedObjectType>> sharedResource;
    
    JUCE_LEAK_DETECTOR (DelayedSharedResourcePointer)
};

The above is untested.


#19

IMHO, this much simpler approach of having this as a (static?) method in your Processor class will do:

static LookAndFeel* getMyLookAndFeel ()
{
    static MyLookAndFeel laf;
    return &laf; 
}

This will create the internal laf object only upon first invocation of getMyLookAndFeel(), and it will be kept around until your plug-in is destructed.

If you are dubious about the fact that laf is actually created only upon first invocation, you could be extra sure at the cost of a small overhead:

static LookAndFeel* getMyLookAndFeel()
{
    static ScopedPointer <LookAndFeel> laf;
    
    if (laf == nullptr)
        laf = new MyLookAndFeel();

    return laf.get(); 
}

#20

My only hesitation to suggesting that in the first place is when it’s deleted rather than constructed, I imagine it will be fine but that’s why I went for the singleton approach using DeletedAtShutdown to ensure it is deleted at the right time.

The more I think about it though, the more I think that by default the SharedResourcePointer shouldn’t actually create anything until something attempts to access the shared resource, otherwise it’s potentially wasteful.